Skip to content

Commit

Permalink
feat: Add analytics metadata to Alert component (#2682)
Browse files Browse the repository at this point in the history
Co-authored-by: Francesco Longo <[email protected]>
  • Loading branch information
fralongo and Francesco Longo authored Sep 10, 2024
1 parent 824074c commit 9c17527
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 6 deletions.
117 changes: 117 additions & 0 deletions src/alert/__tests__/analytics-metadata.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import { render } from '@testing-library/react';

import {
activateAnalyticsMetadata,
GeneratedAnalyticsMetadataFragment,
} from '@cloudscape-design/component-toolkit/internal/analytics-metadata';
import { getGeneratedAnalyticsMetadata } from '@cloudscape-design/component-toolkit/internal/analytics-metadata/utils';

import Alert, { AlertProps } from '../../../lib/components/alert';
import InternalAlert from '../../../lib/components/alert/internal';
import Button from '../../../lib/components/button';
import createWrapper from '../../../lib/components/test-utils/dom';
import { validateComponentNameAndLabels } from '../../internal/__tests__/analytics-metadata-test-utils';

import labels from '../../../lib/components/alert/analytics-metadata/styles.css.js';

function renderAlert(props: AlertProps = {}) {
const renderResult = render(<Alert {...props} />);
return createWrapper(renderResult.container).findAlert()!;
}

const getMetadata = (label: string, type: string) => {
const metadata: GeneratedAnalyticsMetadataFragment = {
contexts: [
{
type: 'component',
detail: {
name: 'awsui.Alert',
label,
properties: {
type,
},
},
},
],
};
return metadata;
};

beforeAll(() => {
activateAnalyticsMetadata(true);
});
describe('Alert renders correct analytics metadata', () => {
test('on dismiss button', () => {
const wrapper = renderAlert({ dismissible: true, dismissAriaLabel: 'dismiss label' });
const dismissButton = wrapper.findDismissButton()!.getElement();
validateComponentNameAndLabels(dismissButton, labels);
expect(getGeneratedAnalyticsMetadata(dismissButton)).toEqual({
action: 'dismiss',
detail: {
label: 'dismiss label',
},
...getMetadata('', 'info'),
});
});

test('on action button', () => {
const wrapper = renderAlert({ buttonText: 'click me' });
const actionButton = wrapper.findActionButton()!.getElement();
validateComponentNameAndLabels(actionButton, labels);
expect(getGeneratedAnalyticsMetadata(actionButton)).toEqual({
action: 'buttonClick',
detail: {
label: 'click me',
},
...getMetadata('', 'info'),
});
});

test('with header', () => {
const wrapper = renderAlert({ header: 'alert header' });
const alertElement = wrapper.getElement();
validateComponentNameAndLabels(alertElement, labels);
expect(getGeneratedAnalyticsMetadata(alertElement)).toEqual(getMetadata('alert header', 'info'));
});

test('with type', () => {
const wrapper = renderAlert({ type: 'error' });
const alertElement = wrapper.getElement();
validateComponentNameAndLabels(alertElement, labels);
expect(getGeneratedAnalyticsMetadata(alertElement)).toEqual(getMetadata('', 'error'));
});

test('with buttons in actions slot', () => {
const wrapper = renderAlert({ action: <Button>another button</Button> });
const buttonElement = wrapper.findActionSlot()!.findButton()!.getElement();
expect(getGeneratedAnalyticsMetadata(buttonElement)).toEqual({
action: 'click',
detail: {
label: 'another button',
},
contexts: [
{
type: 'component',
detail: {
name: 'awsui.Button',
label: 'another button',
properties: {
disabled: 'false',
variant: 'normal',
},
},
},
...getMetadata('', 'info').contexts!,
],
});
});
});

test('Internal Alert does not render "component" metadata', () => {
const renderResult = render(<InternalAlert type="info" />);
const wrapper = createWrapper(renderResult.container).findAlert()!;
expect(getGeneratedAnalyticsMetadata(wrapper.getElement())).toEqual({});
});
15 changes: 12 additions & 3 deletions src/alert/actions-wrapper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import React from 'react';
import clsx from 'clsx';

import { getAnalyticsMetadataAttribute } from '@cloudscape-design/component-toolkit/internal/analytics-metadata';

import InternalButton, { InternalButtonProps } from '../../button/internal';
import { GeneratedAnalyticsMetadataAlertButtonClick } from '../analytics-metadata/interfaces';

import styles from './styles.css.js';

Expand All @@ -15,9 +18,15 @@ function createActionButton(
) {
if (!action && buttonText) {
action = (
<InternalButton className={testUtilClasses.actionButton} onClick={onButtonClick} formAction="none">
{buttonText}
</InternalButton>
<span
{...getAnalyticsMetadataAttribute({
action: 'buttonClick',
} as Partial<GeneratedAnalyticsMetadataAlertButtonClick>)}
>
<InternalButton className={testUtilClasses.actionButton} onClick={onButtonClick} formAction="none">
{buttonText}
</InternalButton>
</span>
);
}
return action ? <div className={testUtilClasses.actionSlot}>{action}</div> : null;
Expand Down
24 changes: 24 additions & 0 deletions src/alert/analytics-metadata/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

export interface GeneratedAnalyticsMetadataAlertDismiss {
action: 'dismiss';
detail: {
label: string;
};
}

export interface GeneratedAnalyticsMetadataAlertButtonClick {
action: 'buttonClick';
detail: {
label: string;
};
}

export interface GeneratedAnalyticsMetadataAlertComponent {
name: 'awsui.Alert';
label: string;
properties: {
type: string;
};
}
8 changes: 8 additions & 0 deletions src/alert/analytics-metadata/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

.header {
/* used in analytics metadata */
}
24 changes: 23 additions & 1 deletion src/alert/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
// SPDX-License-Identifier: Apache-2.0
import React, { useEffect } from 'react';

import { getAnalyticsMetadataAttribute } from '@cloudscape-design/component-toolkit/internal/analytics-metadata';

import { FunnelMetrics } from '../internal/analytics';
import { useFunnel, useFunnelStep, useFunnelSubStep } from '../internal/analytics/hooks/use-funnel';
import { getNameFromSelector, getSubStepAllSelector } from '../internal/analytics/selectors';
import { BasePropsWithAnalyticsMetadata, getAnalyticsMetadataProps } from '../internal/base-component';
import useBaseComponent from '../internal/hooks/use-base-component';
import { applyDisplayName } from '../internal/utils/apply-display-name';
import { GeneratedAnalyticsMetadataAlertComponent } from './analytics-metadata/interfaces';
import { AlertProps } from './interfaces';
import InternalAlert from './internal';

import analyticsSelectors from './analytics-metadata/styles.css.js';

export { AlertProps };

const Alert = React.forwardRef(
Expand Down Expand Up @@ -73,7 +78,24 @@ const Alert = React.forwardRef(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [funnelInteractionId, visible, submissionAttempt, errorCount]);

return <InternalAlert type={type} visible={visible} {...props} {...baseComponentProps} ref={ref} />;
const componentAnalyticsMetadata: GeneratedAnalyticsMetadataAlertComponent = {
name: 'awsui.Alert',
label: `.${analyticsSelectors.header}`,
properties: {
type,
},
};

return (
<InternalAlert
type={type}
visible={visible}
{...props}
{...baseComponentProps}
ref={ref}
{...getAnalyticsMetadataAttribute({ component: componentAnalyticsMetadata })}
/>
);
}
);

Expand Down
13 changes: 11 additions & 2 deletions src/alert/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import React, { useRef } from 'react';
import clsx from 'clsx';

import { getAnalyticsMetadataAttribute } from '@cloudscape-design/component-toolkit/internal/analytics-metadata';

import { InternalButton } from '../button/internal';
import { useInternalI18n } from '../i18n/context';
import { IconProps } from '../icon/interfaces';
Expand All @@ -21,8 +23,10 @@ import { awsuiPluginsInternal } from '../internal/plugins/api';
import { createUseDiscoveredAction } from '../internal/plugins/helpers';
import { SomeRequired } from '../internal/types';
import { ActionsWrapper } from './actions-wrapper';
import { GeneratedAnalyticsMetadataAlertDismiss } from './analytics-metadata/interfaces';
import { AlertProps } from './interfaces';

import analyticsSelectors from './analytics-metadata/styles.css.js';
import styles from './styles.css.js';

const typeToIcon: Record<AlertProps.Type, IconProps['name']> = {
Expand Down Expand Up @@ -101,7 +105,7 @@ const InternalAlert = React.forwardRef(
</div>
<div className={clsx(styles.message, styles.text)}>
{header && (
<div className={styles.header} ref={headerRef}>
<div className={clsx(styles.header, analyticsSelectors.header)} ref={headerRef}>
{header}
</div>
)}
Expand All @@ -122,7 +126,12 @@ const InternalAlert = React.forwardRef(
onButtonClick={() => fireNonCancelableEvent(onButtonClick)}
/>
{dismissible && (
<div className={styles.dismiss}>
<div
className={styles.dismiss}
{...getAnalyticsMetadataAttribute({
action: 'dismiss',
} as Partial<GeneratedAnalyticsMetadataAlertDismiss>)}
>
<InternalButton
className={styles['dismiss-button']}
variant="icon"
Expand Down

0 comments on commit 9c17527

Please sign in to comment.