123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- import {useCallback, useMemo} from 'react';
- import {useSortable} from '@dnd-kit/sortable';
- import styled from '@emotion/styled';
- import Feature from 'sentry/components/acl/feature';
- import FeatureBadge from 'sentry/components/badge/featureBadge';
- import type {ButtonProps} from 'sentry/components/button';
- import {Button} from 'sentry/components/button';
- import DropdownButton from 'sentry/components/dropdownButton';
- import type {MenuItemProps} from 'sentry/components/dropdownMenu';
- import {DropdownMenu} from 'sentry/components/dropdownMenu';
- import ExternalLink from 'sentry/components/links/externalLink';
- import {IconAdd} from 'sentry/icons';
- import {t} from 'sentry/locale';
- import {space} from 'sentry/styles/space';
- import {trackAnalytics} from 'sentry/utils/analytics';
- import {hasCustomMetrics} from 'sentry/utils/metrics/features';
- import useOrganization from 'sentry/utils/useOrganization';
- import {DataSet} from 'sentry/views/dashboards/widgetBuilder/utils';
- import {DisplayType} from './types';
- import WidgetWrapper from './widgetWrapper';
- export const ADD_WIDGET_BUTTON_DRAG_ID = 'add-widget-button';
- const initialStyles = {
- x: 0,
- y: 0,
- scaleX: 1,
- scaleY: 1,
- };
- type Props = {
- onAddWidget: (dataset: DataSet) => void;
- };
- function AddWidget({onAddWidget}: Props) {
- const {setNodeRef, transform} = useSortable({
- disabled: true,
- id: ADD_WIDGET_BUTTON_DRAG_ID,
- transition: null,
- });
- const organization = useOrganization();
- const defaultDataset = organization.features.includes(
- 'performance-discover-dataset-selector'
- )
- ? DataSet.ERRORS
- : DataSet.EVENTS;
- return (
- <Feature features="dashboards-edit">
- <WidgetWrapper
- key="add"
- ref={setNodeRef}
- displayType={DisplayType.BIG_NUMBER}
- layoutId={ADD_WIDGET_BUTTON_DRAG_ID}
- style={{originX: 0, originY: 0}}
- animate={
- transform
- ? {
- x: transform.x,
- y: transform.y,
- scaleX: transform?.scaleX && transform.scaleX <= 1 ? transform.scaleX : 1,
- scaleY: transform?.scaleY && transform.scaleY <= 1 ? transform.scaleY : 1,
- }
- : initialStyles
- }
- transition={{
- duration: 0.25,
- }}
- >
- {hasCustomMetrics(organization) ? (
- <InnerWrapper>
- <AddWidgetButton
- onAddWidget={onAddWidget}
- aria-label={t('Add Widget')}
- data-test-id="widget-add"
- />
- </InnerWrapper>
- ) : (
- <InnerWrapper onClick={() => onAddWidget(defaultDataset)}>
- <AddButton
- data-test-id="widget-add"
- icon={<IconAdd size="lg" isCircled color="inactive" />}
- aria-label={t('Add widget')}
- />
- </InnerWrapper>
- )}
- </WidgetWrapper>
- </Feature>
- );
- }
- const AddButton = styled(Button)`
- border: none;
- &,
- &:focus,
- &:active,
- &:hover {
- background: transparent;
- box-shadow: none;
- }
- `;
- export default AddWidget;
- export function AddWidgetButton({onAddWidget, ...buttonProps}: Props & ButtonProps) {
- const organization = useOrganization();
- const handleAction = useCallback(
- (dataset: DataSet) => {
- trackAnalytics('dashboards_views.widget_library.opened', {
- organization,
- });
- onAddWidget(dataset);
- },
- [organization, onAddWidget]
- );
- const items = useMemo(() => {
- const menuItems: MenuItemProps[] = [];
- if (organization.features.includes('performance-discover-dataset-selector')) {
- menuItems.push({
- key: DataSet.ERRORS,
- label: t('Errors'),
- onAction: () => handleAction(DataSet.ERRORS),
- });
- menuItems.push({
- key: DataSet.TRANSACTIONS,
- label: t('Transactions'),
- onAction: () => handleAction(DataSet.TRANSACTIONS),
- });
- } else {
- menuItems.push({
- key: DataSet.EVENTS,
- label: t('Errors and Transactions'),
- onAction: () => handleAction(DataSet.EVENTS),
- });
- }
- menuItems.push({
- key: DataSet.ISSUES,
- label: t('Issues'),
- details: t('States, Assignment, Time, etc.'),
- onAction: () => handleAction(DataSet.ISSUES),
- });
- menuItems.push({
- key: DataSet.RELEASES,
- label: t('Releases'),
- details: t('Sessions, Crash rates, etc.'),
- onAction: () => handleAction(DataSet.RELEASES),
- });
- if (hasCustomMetrics(organization)) {
- menuItems.push({
- key: DataSet.METRICS,
- label: t('Metrics'),
- onAction: () => handleAction(DataSet.METRICS),
- trailingItems: (
- <FeatureBadge
- type="beta"
- title={
- 'The Metrics beta will end and we will retire the current solution on October 7th, 2024'
- }
- />
- ),
- });
- }
- return menuItems;
- }, [handleAction, organization]);
- return (
- <DropdownMenu
- items={items}
- trigger={triggerProps => (
- <DropdownButton
- {...triggerProps}
- {...buttonProps}
- data-test-id="widget-add"
- size="sm"
- icon={<IconAdd isCircled />}
- >
- {t('Add Widget')}
- </DropdownButton>
- )}
- menuTitle={
- <MenuTitle>
- {t('Dataset')}
- <ExternalLink href="https://docs.sentry.io/product/dashboards/widget-builder/#choose-your-dataset">
- {t('Learn more')}
- </ExternalLink>
- </MenuTitle>
- }
- />
- );
- }
- const InnerWrapper = styled('div')<{onClick?: () => void}>`
- width: 100%;
- height: 110px;
- border: 2px dashed ${p => p.theme.border};
- border-radius: ${p => p.theme.borderRadius};
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: ${p => (p.onClick ? 'pointer' : '')};
- `;
- const MenuTitle = styled('span')`
- display: flex;
- gap: ${space(1)};
- & > a {
- font-weight: ${p => p.theme.fontWeightNormal};
- }
- `;
|