@atlaskit/flag 14.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +1244 -0
  2. package/LICENSE +13 -0
  3. package/README.md +13 -0
  4. package/__perf__/autodismiss.tsx +18 -0
  5. package/__perf__/default.tsx +18 -0
  6. package/__perf__/withFlagGroup.tsx +20 -0
  7. package/auto-dismiss-flag/package.json +7 -0
  8. package/constants/package.json +7 -0
  9. package/dist/cjs/auto-dismiss-flag.js +89 -0
  10. package/dist/cjs/constants.js +8 -0
  11. package/dist/cjs/expander.js +41 -0
  12. package/dist/cjs/flag-actions.js +64 -0
  13. package/dist/cjs/flag-group.js +107 -0
  14. package/dist/cjs/flag-provider.js +119 -0
  15. package/dist/cjs/flag.js +219 -0
  16. package/dist/cjs/index.js +51 -0
  17. package/dist/cjs/theme.js +160 -0
  18. package/dist/cjs/types.js +10 -0
  19. package/dist/cjs/version.json +5 -0
  20. package/dist/es2019/auto-dismiss-flag.js +67 -0
  21. package/dist/es2019/constants.js +1 -0
  22. package/dist/es2019/expander.js +33 -0
  23. package/dist/es2019/flag-actions.js +73 -0
  24. package/dist/es2019/flag-group.js +130 -0
  25. package/dist/es2019/flag-provider.js +68 -0
  26. package/dist/es2019/flag.js +245 -0
  27. package/dist/es2019/index.js +4 -0
  28. package/dist/es2019/theme.js +119 -0
  29. package/dist/es2019/types.js +2 -0
  30. package/dist/es2019/version.json +5 -0
  31. package/dist/esm/auto-dismiss-flag.js +67 -0
  32. package/dist/esm/constants.js +1 -0
  33. package/dist/esm/expander.js +29 -0
  34. package/dist/esm/flag-actions.js +50 -0
  35. package/dist/esm/flag-group.js +85 -0
  36. package/dist/esm/flag-provider.js +92 -0
  37. package/dist/esm/flag.js +195 -0
  38. package/dist/esm/index.js +4 -0
  39. package/dist/esm/theme.js +129 -0
  40. package/dist/esm/types.js +2 -0
  41. package/dist/esm/version.json +5 -0
  42. package/dist/types/auto-dismiss-flag.d.ts +4 -0
  43. package/dist/types/constants.d.ts +2 -0
  44. package/dist/types/expander.d.ts +9 -0
  45. package/dist/types/flag-actions.d.ts +14 -0
  46. package/dist/types/flag-group.d.ts +27 -0
  47. package/dist/types/flag-provider.d.ts +26 -0
  48. package/dist/types/flag.d.ts +3 -0
  49. package/dist/types/index.d.ts +6 -0
  50. package/dist/types/theme.d.ts +9 -0
  51. package/dist/types/types.d.ts +79 -0
  52. package/expander/package.json +7 -0
  53. package/extract-react-types/show-flag-args.tsx +5 -0
  54. package/flag/package.json +7 -0
  55. package/flag-actions/package.json +7 -0
  56. package/flag-group/package.json +7 -0
  57. package/flag-provider/package.json +7 -0
  58. package/package.json +80 -0
  59. package/theme/package.json +7 -0
  60. package/types/package.json +7 -0
@@ -0,0 +1,67 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { useCallback, useEffect, useRef } from 'react';
3
+ import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler';
4
+ import Flag from './flag';
5
+ import { useFlagGroup } from './flag-group';
6
+ const packageName = "@atlaskit/flag";
7
+ const packageVersion = "14.4.1";
8
+ export const AUTO_DISMISS_SECONDS = 8;
9
+
10
+ function noop() {}
11
+
12
+ const AutoDismissFlag = props => {
13
+ const {
14
+ id,
15
+ analyticsContext,
16
+ onDismissed: onDismissedProp = noop
17
+ } = props;
18
+ const autoDismissTimer = useRef(null);
19
+ const {
20
+ onDismissed: onDismissedFromFlagGroup,
21
+ isDismissAllowed
22
+ } = useFlagGroup();
23
+ const onDismissed = useCallback((id, analyticsEvent) => {
24
+ onDismissedProp(id, analyticsEvent);
25
+ onDismissedFromFlagGroup(id, analyticsEvent);
26
+ }, [onDismissedProp, onDismissedFromFlagGroup]);
27
+ const onDismissedAnalytics = usePlatformLeafEventHandler({
28
+ fn: onDismissed,
29
+ action: 'dismissed',
30
+ analyticsData: analyticsContext,
31
+ componentName: 'flag',
32
+ packageName,
33
+ packageVersion
34
+ });
35
+ const isAutoDismissAllowed = isDismissAllowed && onDismissed;
36
+ const dismissFlag = useCallback(() => {
37
+ if (isAutoDismissAllowed) {
38
+ onDismissedAnalytics(id);
39
+ }
40
+ }, [id, onDismissedAnalytics, isAutoDismissAllowed]);
41
+ const stopAutoDismissTimer = useCallback(() => {
42
+ if (autoDismissTimer.current) {
43
+ clearTimeout(autoDismissTimer.current);
44
+ autoDismissTimer.current = null;
45
+ }
46
+ }, []);
47
+ const startAutoDismissTimer = useCallback(() => {
48
+ if (!isAutoDismissAllowed) {
49
+ return;
50
+ }
51
+
52
+ stopAutoDismissTimer();
53
+ autoDismissTimer.current = window.setTimeout(dismissFlag, AUTO_DISMISS_SECONDS * 1000);
54
+ }, [dismissFlag, stopAutoDismissTimer, isAutoDismissAllowed]);
55
+ useEffect(() => {
56
+ startAutoDismissTimer();
57
+ return stopAutoDismissTimer;
58
+ }, [startAutoDismissTimer, stopAutoDismissTimer]);
59
+ return /*#__PURE__*/React.createElement(Flag, _extends({}, props, {
60
+ onMouseOver: stopAutoDismissTimer,
61
+ onFocus: stopAutoDismissTimer,
62
+ onMouseOut: startAutoDismissTimer,
63
+ onBlur: startAutoDismissTimer
64
+ }));
65
+ };
66
+
67
+ export default AutoDismissFlag;
@@ -0,0 +1 @@
1
+ export const DEFAULT_APPEARANCE = 'normal';
@@ -0,0 +1,33 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/core';
3
+ import { ExitingPersistence, FadeIn } from '@atlaskit/motion';
4
+ import { gridSize } from '@atlaskit/theme/constants';
5
+ const paddingLeft = gridSize() * 5;
6
+
7
+ const Expander = ({
8
+ children,
9
+ isExpanded,
10
+ testId
11
+ }) => {
12
+ // Need to always render the ExpanderInternal otherwise the
13
+ // reveal transition doesn't happen. We can't use CSS animation for
14
+ // the the reveal because we don't know the height of the content.
15
+ return jsx("div", {
16
+ "aria-hidden": !isExpanded,
17
+ css: css`
18
+ max-height: ${isExpanded ? 150 : 0}px;
19
+ transition: max-height 0.3s;
20
+ display: flex;
21
+ flex: 1 1 100%;
22
+ flex-direction: column;
23
+ justify-content: center;
24
+ min-width: 0;
25
+ padding: 0 0 0 ${paddingLeft}px;
26
+ `,
27
+ "data-testid": testId && `${testId}-expander`
28
+ }, jsx(ExitingPersistence, {
29
+ appear: true
30
+ }, isExpanded && jsx(FadeIn, null, props => jsx("div", props, children))));
31
+ };
32
+
33
+ export default Expander;
@@ -0,0 +1,73 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/core';
3
+ import Button from '@atlaskit/button/custom-theme-button';
4
+ import { gridSize as getGridSize } from '@atlaskit/theme/constants';
5
+ import { DEFAULT_APPEARANCE } from './constants';
6
+ import { getActionBackground, getActionColor, getFlagFocusRingColor } from './theme';
7
+ const gridSize = getGridSize();
8
+ const separatorWidth = gridSize * 2;
9
+ const defaultAppearanceTranslate = gridSize / 4;
10
+
11
+ const FlagActions = props => {
12
+ const {
13
+ appearance = DEFAULT_APPEARANCE,
14
+ actions = [],
15
+ linkComponent,
16
+ mode,
17
+ testId
18
+ } = props;
19
+
20
+ if (!actions.length) {
21
+ return null;
22
+ }
23
+
24
+ const isBold = appearance !== DEFAULT_APPEARANCE;
25
+ return jsx("div", {
26
+ css: css`
27
+ display: flex;
28
+ flex-wrap: wrap;
29
+ padding-top: ${gridSize}px;
30
+ transform: ${appearance === DEFAULT_APPEARANCE ? `translateX(-${defaultAppearanceTranslate}px)` : 0};
31
+ align-items: center;
32
+ `,
33
+ "data-testid": testId && `${testId}-actions`
34
+ }, actions.map((action, index) => [index && !isBold ? jsx("div", {
35
+ css: css`
36
+ text-align: center;
37
+ display: inline-block;
38
+ width: ${separatorWidth}px;
39
+ `,
40
+ key: index + 0.5
41
+ }, "\xB7") : '', jsx(Button, {
42
+ onClick: action.onClick,
43
+ href: action.href,
44
+ target: action.target,
45
+ appearance: isBold ? 'default' : 'link',
46
+ component: linkComponent,
47
+ spacing: "compact",
48
+ testId: action.testId,
49
+ key: index,
50
+ css: css`
51
+ &&,
52
+ a&& {
53
+ margin-left: ${index && isBold ? gridSize : 0}px;
54
+ font-weight: 500;
55
+ padding: 0 ${appearance === 'normal' ? 0 : gridSize}px !important;
56
+ background: ${getActionBackground(appearance, mode)};
57
+ color: ${getActionColor(appearance, mode)} !important;
58
+ }
59
+ &&:focus,
60
+ a&&:focus {
61
+ box-shadow: 0 0 0 2px ${getFlagFocusRingColor(appearance, mode)};
62
+ }
63
+ &&:hover,
64
+ &&:active,
65
+ a&&:hover,
66
+ a&&:active {
67
+ text-decoration: underline;
68
+ }
69
+ `
70
+ }, action.content)]));
71
+ };
72
+
73
+ export default FlagActions;
@@ -0,0 +1,130 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+
3
+ /** @jsx jsx */
4
+ import { Children, createContext, useContext, useMemo } from 'react';
5
+ import { css, jsx } from '@emotion/core';
6
+ import { easeIn, ExitingPersistence, SlideIn } from '@atlaskit/motion';
7
+ import Portal from '@atlaskit/portal';
8
+ import { gridSize as getGridSize, layers } from '@atlaskit/theme/constants';
9
+ const gridSize = getGridSize();
10
+ export const flagWidth = gridSize * 50;
11
+ export const flagAnimationTime = 400;
12
+ const flagBottom = gridSize * 6;
13
+ const flagLeft = gridSize * 10;
14
+
15
+ function noop() {}
16
+
17
+ const defaultFlagGroupContext = {
18
+ onDismissed: () => {},
19
+ isDismissAllowed: false
20
+ };
21
+ export const FlagGroupContext = /*#__PURE__*/createContext(defaultFlagGroupContext);
22
+ export function useFlagGroup() {
23
+ return useContext(FlagGroupContext);
24
+ } // transition: none is set on first-of-type to prevent a bug in Firefox
25
+ // that causes a broken transition
26
+
27
+ const baseStyles = `
28
+ bottom: 0;
29
+ position: absolute;
30
+ width: ${flagWidth}px;
31
+ transition: transform ${flagAnimationTime}ms ease-in-out;
32
+
33
+ @media (max-width: 560px) {
34
+ width: 100vw;
35
+ }
36
+
37
+ &:first-of-type {
38
+ transition: none;
39
+ transform: translate(0,0);
40
+ }
41
+
42
+ &:nth-of-type(n + 2) {
43
+ animation-duration: 0ms;
44
+ transform: translateX(0) translateY(100%) translateY(${2 * gridSize}px);
45
+ }
46
+
47
+ /* Layer the 'primary' flag above the 'secondary' flag */
48
+ &:nth-of-type(1) {
49
+ z-index: 5;
50
+ }
51
+ &:nth-of-type(2) {
52
+ z-index: 4;
53
+ }
54
+
55
+ &:nth-of-type(n + 4) {
56
+ visibility: hidden;
57
+ }
58
+ `;
59
+
60
+ const FlagGroup = props => {
61
+ const {
62
+ id,
63
+ label = 'Flag notifications',
64
+ labelTag: LabelTag = 'h2',
65
+ children,
66
+ onDismissed = noop
67
+ } = props;
68
+ const hasFlags = Array.isArray(children) ? children.length > 0 : Boolean(children);
69
+ const dismissFlagContext = useMemo(() => ({
70
+ onDismissed: onDismissed,
71
+ isDismissAllowed: true
72
+ }), [onDismissed]);
73
+
74
+ const renderChildren = () => {
75
+ return children && typeof children === 'object' ? Children.map(children, (flag, index) => {
76
+ const isDismissAllowed = index === 0;
77
+ return jsx(SlideIn, {
78
+ enterFrom: 'left',
79
+ fade: 'inout',
80
+ duration: flagAnimationTime,
81
+ animationTimingFunction: () => easeIn
82
+ }, props => jsx("div", _extends({}, props, {
83
+ css: css`
84
+ ${baseStyles}
85
+ ${isDismissAllowed ? // Transform needed to push up while 1st flag is leaving
86
+ // Exiting time should match the exiting time of motion so is halved
87
+ `
88
+ && + * {
89
+ transform: translate(0, 0);
90
+ transition-duration: ${flagAnimationTime / 2}ms
91
+ }` : ''}
92
+ `
93
+ }), jsx(FlagGroupContext.Provider, {
94
+ value: // Only the first flag should be able to be dismissed.
95
+ isDismissAllowed ? dismissFlagContext : defaultFlagGroupContext
96
+ }, flag)));
97
+ }) : false;
98
+ };
99
+
100
+ return jsx(Portal, {
101
+ zIndex: layers.flag()
102
+ }, jsx("div", {
103
+ id: id,
104
+ css: css`
105
+ bottom: ${flagBottom}px;
106
+ left: ${flagLeft}px;
107
+ position: fixed;
108
+ z-index: ${layers.flag()};
109
+ @media (max-width: 560px) {
110
+ bottom: 0;
111
+ left: 0;
112
+ }
113
+ `
114
+ }, hasFlags ? jsx(LabelTag, {
115
+ css: css`
116
+ border: 0;
117
+ clip: rect(1px, 1px, 1px, 1px);
118
+ height: 1px;
119
+ overflow: hidden;
120
+ padding: 0;
121
+ position: absolute;
122
+ white-space: nowrap;
123
+ width: 1px;
124
+ `
125
+ }, label) : null, jsx(ExitingPersistence, {
126
+ appear: false
127
+ }, renderChildren())));
128
+ };
129
+
130
+ export default FlagGroup;
@@ -0,0 +1,68 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { useCallback, useContext, useMemo, useState } from 'react';
3
+ import AutoDismissFlag from './auto-dismiss-flag';
4
+ import Flag from './flag';
5
+ import FlagGroup from './flag-group';
6
+ const FlagContext = /*#__PURE__*/React.createContext(null);
7
+ export function useFlags() {
8
+ const api = useContext(FlagContext);
9
+
10
+ if (api == null) {
11
+ throw new Error('Unable to find FlagProviderContext');
12
+ }
13
+
14
+ return api;
15
+ }
16
+
17
+ const getUniqueId = (() => {
18
+ let count = 0;
19
+ return () => `flag-provider-unique-id:${count++}`;
20
+ })();
21
+
22
+ export function FlagsProvider({
23
+ children
24
+ }) {
25
+ const [flags, setFlags] = useState([]);
26
+ const removeFlag = useCallback(id => {
27
+ setFlags(current => {
28
+ return current.slice(0).filter(flag => flag.id !== id);
29
+ });
30
+ }, []);
31
+ const api = useMemo(() => ({
32
+ showFlag: function show(value) {
33
+ const flag = { ...value,
34
+ id: value.id || getUniqueId()
35
+ };
36
+ setFlags(current => {
37
+ const index = current.findIndex(value => value.id === flag.id); // If flag is not found add it
38
+
39
+ if (index === -1) {
40
+ return [flag, ...current];
41
+ } // If flag already exists with the same id, then replace it
42
+
43
+
44
+ const shallow = [...current];
45
+ shallow[index] = flag;
46
+ return shallow;
47
+ });
48
+ return function dismiss() {
49
+ removeFlag(flag.id);
50
+ };
51
+ }
52
+ }), [removeFlag]);
53
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FlagContext.Provider, {
54
+ value: api
55
+ }, children), /*#__PURE__*/React.createElement(FlagGroup, {
56
+ onDismissed: removeFlag
57
+ }, flags.map(flag => {
58
+ const {
59
+ isAutoDismiss,
60
+ ...restProps
61
+ } = flag;
62
+ const FlagType = isAutoDismiss ? AutoDismissFlag : Flag;
63
+ return /*#__PURE__*/React.createElement(FlagType, _extends({}, restProps, {
64
+ key: flag.id
65
+ }));
66
+ })));
67
+ }
68
+ export const withFlagsProvider = fn => /*#__PURE__*/React.createElement(FlagsProvider, null, fn());
@@ -0,0 +1,245 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+
3
+ /** @jsx jsx */
4
+ import { useCallback, useEffect, useState } from 'react';
5
+ import { css, jsx } from '@emotion/core';
6
+ import ChevronDownIcon from '@atlaskit/icon/glyph/chevron-down';
7
+ import ChevronUpIcon from '@atlaskit/icon/glyph/chevron-up';
8
+ import CrossIcon from '@atlaskit/icon/glyph/cross';
9
+ import GlobalTheme from '@atlaskit/theme/components';
10
+ import { borderRadius, gridSize as getGridSize, layers } from '@atlaskit/theme/constants';
11
+ import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler';
12
+ import { DEFAULT_APPEARANCE } from './constants';
13
+ import { flagBorderColor, flagShadowColor, getFlagBackgroundColor, getFlagFocusRingColor, getFlagTextColor } from './theme';
14
+ import Expander from './expander';
15
+ import Actions from './flag-actions';
16
+ import { useFlagGroup } from './flag-group';
17
+
18
+ function noop() {}
19
+
20
+ const analyticsAttributes = {
21
+ componentName: 'flag',
22
+ packageName: "@atlaskit/flag",
23
+ packageVersion: "14.4.1"
24
+ };
25
+ const gridSize = getGridSize();
26
+ const doubleGridSize = gridSize * 2;
27
+ const headerHeight = gridSize * 4;
28
+
29
+ const Flag = props => {
30
+ const {
31
+ actions = [],
32
+ appearance = DEFAULT_APPEARANCE,
33
+ icon,
34
+ title,
35
+ description,
36
+ linkComponent,
37
+ onMouseOver,
38
+ onFocus = noop,
39
+ onMouseOut,
40
+ onBlur = noop,
41
+ onDismissed: onDismissedProp = noop,
42
+ testId,
43
+ id,
44
+ analyticsContext
45
+ } = props;
46
+ const {
47
+ onDismissed: onDismissedFromFlagGroup,
48
+ isDismissAllowed
49
+ } = useFlagGroup();
50
+ const onDismissed = useCallback((id, analyticsEvent) => {
51
+ onDismissedProp(id, analyticsEvent);
52
+ onDismissedFromFlagGroup(id, analyticsEvent);
53
+ }, [onDismissedProp, onDismissedFromFlagGroup]);
54
+ const [isExpanded, setIsExpanded] = useState(false);
55
+ const onDismissedAnalytics = usePlatformLeafEventHandler({
56
+ fn: onDismissed,
57
+ action: 'dismissed',
58
+ analyticsData: analyticsContext,
59
+ ...analyticsAttributes
60
+ });
61
+ const isBold = appearance !== DEFAULT_APPEARANCE;
62
+ const renderToggleOrDismissButton = useCallback(({
63
+ mode
64
+ }) => {
65
+ // If it is normal appearance a toggle button cannot be rendered
66
+ // Ensure onDismissed is defined and isDismissAllowed is true to render
67
+ // the dismiss button
68
+ if (!isBold && !isDismissAllowed) {
69
+ return null;
70
+ } // If it is bold then ensure there is a description or actions to render
71
+ // the toggle button
72
+
73
+
74
+ if (isBold && !description && !actions.length) {
75
+ return null;
76
+ }
77
+
78
+ let ButtonIcon = CrossIcon;
79
+ let buttonLabel = 'Dismiss';
80
+
81
+ let buttonAction = () => {
82
+ if (isDismissAllowed) {
83
+ onDismissedAnalytics(id);
84
+ }
85
+ };
86
+
87
+ let size = 'small';
88
+ let buttonTestId = testId && `${testId}-dismiss`;
89
+ let a11yProps = {};
90
+
91
+ if (isBold) {
92
+ ButtonIcon = isExpanded ? ChevronUpIcon : ChevronDownIcon;
93
+ buttonLabel = isExpanded ? 'Collapse' : 'Expand';
94
+
95
+ buttonAction = () => setIsExpanded(!isExpanded);
96
+
97
+ size = 'large';
98
+ buttonTestId = testId && `${testId}-toggle`;
99
+ a11yProps = {
100
+ 'aria-expanded': isExpanded
101
+ };
102
+ }
103
+
104
+ return jsx("button", _extends({
105
+ css: css`
106
+ appearance: none;
107
+ background: none;
108
+ border: none;
109
+ border-radius: ${borderRadius()}px;
110
+ color: ${getFlagTextColor(appearance, mode)};
111
+ cursor: pointer;
112
+ flex: 0 0 auto;
113
+ line-height: 1;
114
+ margin-left: ${gridSize}px;
115
+ padding: 0;
116
+ white-space: nowrap;
117
+ &:focus {
118
+ outline: none;
119
+ box-shadow: 0 0 0 2px ${getFlagFocusRingColor(appearance, mode)};
120
+ }
121
+ `,
122
+ onClick: buttonAction,
123
+ "data-testid": buttonTestId,
124
+ type: "button"
125
+ }, a11yProps), jsx(ButtonIcon, {
126
+ label: buttonLabel,
127
+ size: size
128
+ }));
129
+ }, [actions.length, appearance, description, id, isBold, isDismissAllowed, isExpanded, onDismissedAnalytics, testId]);
130
+ useEffect(() => {
131
+ // If buttons are removed as a prop, update isExpanded to be false
132
+ if (isBold && isExpanded && !description && !actions.length) {
133
+ setIsExpanded(false);
134
+ }
135
+ }, [actions.length, description, isBold, isExpanded]);
136
+ const onFocusAnalytics = usePlatformLeafEventHandler({
137
+ fn: onFocus,
138
+ action: 'focused',
139
+ analyticsData: analyticsContext,
140
+ ...analyticsAttributes
141
+ });
142
+ const onBlurAnalytics = usePlatformLeafEventHandler({
143
+ fn: onBlur,
144
+ action: 'blurred',
145
+ analyticsData: analyticsContext,
146
+ ...analyticsAttributes
147
+ });
148
+ const autoDismissProps = {
149
+ onMouseOver,
150
+ onFocus: onFocusAnalytics,
151
+ onMouseOut,
152
+ onBlur: onBlurAnalytics
153
+ };
154
+ const OptionalDismissButton = renderToggleOrDismissButton;
155
+ let boxShadow = `0 20px 32px -8px ${flagShadowColor}`;
156
+
157
+ if (!isBold) {
158
+ boxShadow = `0 0 1px ${flagBorderColor}, ${boxShadow}`;
159
+ }
160
+
161
+ return jsx(GlobalTheme.Consumer, null, tokens => {
162
+ const mode = tokens.mode;
163
+ const textColour = getFlagTextColor(appearance, mode);
164
+ return jsx("div", _extends({
165
+ css: css`
166
+ background-color: ${getFlagBackgroundColor(appearance, mode)};
167
+ border-radius: ${borderRadius()}px;
168
+ box-shadow: ${boxShadow};
169
+ color: ${textColour};
170
+ transition: background-color 200ms;
171
+ width: 100%;
172
+ z-index: ${layers.flag()};
173
+ `,
174
+ role: "alert",
175
+ "data-testid": testId
176
+ }, autoDismissProps), jsx("div", {
177
+ css: css`
178
+ width: 100%;
179
+ padding: ${doubleGridSize}px;
180
+ box-sizing: border-box;
181
+ border-radius: ${borderRadius()}px;
182
+
183
+ &:focus-visible {
184
+ outline: none;
185
+ box-shadow: 0 0 0 2px
186
+ ${getFlagFocusRingColor(appearance, mode)};
187
+ }
188
+
189
+ @supports not selector(*:focus-visible) {
190
+ &:focus {
191
+ outline: none;
192
+ box-shadow: 0 0 0 2px
193
+ ${getFlagFocusRingColor(appearance, mode)};
194
+ }
195
+ }
196
+
197
+ @media screen and (forced-colors: active),
198
+ screen and (-ms-high-contrast: active) {
199
+ &:focus-visible {
200
+ outline: 1px solid;
201
+ }
202
+ }
203
+ ` // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
204
+ ,
205
+ tabIndex: 0
206
+ }, jsx("div", {
207
+ css: css`
208
+ display: flex;
209
+ align-items: center;
210
+ height: ${headerHeight}px;
211
+ `
212
+ }, icon, jsx("span", {
213
+ css: css`
214
+ color: ${textColour};
215
+ font-weight: 600;
216
+ flex: 1;
217
+ overflow: hidden;
218
+ text-overflow: ellipsis;
219
+ white-space: nowrap;
220
+ padding: 0 0 0 ${doubleGridSize}px;
221
+ `
222
+ }, title), jsx(OptionalDismissButton, {
223
+ mode: mode
224
+ })), jsx(Expander, {
225
+ isExpanded: !isBold || isExpanded,
226
+ testId: testId
227
+ }, description && jsx("div", {
228
+ css: css`
229
+ color: ${textColour};
230
+ word-wrap: break-word;
231
+ overflow: auto;
232
+ max-height: 100px; /* height is defined as 5 lines maximum by design */
233
+ `,
234
+ "data-testid": testId && `${testId}-description`
235
+ }, description), jsx(Actions, {
236
+ actions: actions,
237
+ appearance: appearance,
238
+ linkComponent: linkComponent,
239
+ testId: testId,
240
+ mode: mode
241
+ }))));
242
+ });
243
+ };
244
+
245
+ export default Flag;