@kaizen/components 3.0.14 → 3.1.0

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.
@@ -29,15 +29,17 @@ var BrandMoment = function (_a) {
29
29
  body = _a.body,
30
30
  primaryAction = _a.primaryAction,
31
31
  secondaryAction = _a.secondaryAction,
32
+ tag = _a.tag,
32
33
  text = _a.text,
33
34
  classNameOverride = _a.classNameOverride,
34
- restProps = tslib.__rest(_a, ["variant", "illustration", "header", "body", "primaryAction", "secondaryAction", "text", "classNameOverride"]);
35
+ restProps = tslib.__rest(_a, ["variant", "illustration", "header", "body", "primaryAction", "secondaryAction", "tag", "text", "classNameOverride"]);
35
36
  var queries = useMediaQueries.useMediaQueries().queries;
37
+ var Tag = tag !== null && tag !== void 0 ? tag : 'main';
36
38
  return React__default.default.createElement("div", tslib.__assign({
37
39
  className: classnames__default.default(BrandMoment_module.body, BrandMoment_module[variant], classNameOverride)
38
40
  }, restProps), React__default.default.createElement("header", {
39
41
  className: BrandMoment_module.header
40
- }, header), React__default.default.createElement("main", {
42
+ }, header), React__default.default.createElement(Tag, {
41
43
  className: BrandMoment_module.main
42
44
  }, React__default.default.createElement("div", {
43
45
  className: BrandMoment_module.container
@@ -87,12 +87,16 @@ var Collapsible = function (_a) {
87
87
  classNameOverride: Collapsible_module.chevronButton
88
88
  }))), (!lazyLoad || isOpen) && React__default.default.createElement(AnimateHeight__default.default, {
89
89
  height: isOpen ? 'auto' : 0,
90
+ disableDisplayNone: true,
90
91
  "data-testid": "collapsible-section-".concat(id)
91
92
  }, React__default.default.createElement("div", {
92
93
  id: sectionId,
93
94
  className: classnames__default.default(Collapsible_module.section, noSectionPadding && Collapsible_module.noPadding),
94
95
  role: "region",
95
- "aria-labelledby": buttonId
96
+ "aria-labelledby": buttonId,
97
+ // TODO: Remove @ts-expect-error when upgrade to @types/react@19 that support the `inert` HTML attribute
98
+ // @ts-expect-error current react types don't yet support the `inert` HTML attribute
99
+ inert: isOpen ? undefined : true
96
100
  }, children)));
97
101
  };
98
102
  Collapsible.displayName = 'Collapsible';
@@ -32,6 +32,7 @@ var ErrorPage = function (_a) {
32
32
  title = _a.title,
33
33
  message = _a.message,
34
34
  callToAction = _a.callToAction,
35
+ tag = _a.tag,
35
36
  classNameOverride = _a.classNameOverride;
36
37
  var formatMessage = i18nReactIntl.useIntl().formatMessage;
37
38
  var content = useErrorMessages.useErrorMessages(code);
@@ -48,6 +49,7 @@ var ErrorPage = function (_a) {
48
49
  return React__default.default.createElement("div", {
49
50
  className: classnames__default.default(classNameOverride)
50
51
  }, React__default.default.createElement(BrandMoment.BrandMoment, {
52
+ tag: tag,
51
53
  header: React__default.default.createElement(React__default.default.Fragment, null),
52
54
  body: React__default.default.createElement(React__default.default.Fragment, null, React__default.default.createElement("div", {
53
55
  className: ErrorPage_module.paragraphPadding
@@ -21,15 +21,17 @@ const BrandMoment = /*#__PURE__*/function () {
21
21
  body = _a.body,
22
22
  primaryAction = _a.primaryAction,
23
23
  secondaryAction = _a.secondaryAction,
24
+ tag = _a.tag,
24
25
  text = _a.text,
25
26
  classNameOverride = _a.classNameOverride,
26
- restProps = __rest(_a, ["variant", "illustration", "header", "body", "primaryAction", "secondaryAction", "text", "classNameOverride"]);
27
+ restProps = __rest(_a, ["variant", "illustration", "header", "body", "primaryAction", "secondaryAction", "tag", "text", "classNameOverride"]);
27
28
  var queries = useMediaQueries().queries;
29
+ var Tag = tag !== null && tag !== void 0 ? tag : 'main';
28
30
  return /*#__PURE__*/React.createElement("div", __assign({
29
31
  className: classnames(modules_5e37dab3.body, modules_5e37dab3[variant], classNameOverride)
30
32
  }, restProps), /*#__PURE__*/React.createElement("header", {
31
33
  className: modules_5e37dab3.header
32
- }, header), /*#__PURE__*/React.createElement("main", {
34
+ }, header), /*#__PURE__*/React.createElement(Tag, {
33
35
  className: modules_5e37dab3.main
34
36
  }, /*#__PURE__*/React.createElement("div", {
35
37
  className: modules_5e37dab3.container
@@ -78,12 +78,16 @@ const Collapsible = /*#__PURE__*/function () {
78
78
  classNameOverride: modules_23a5f9ec.chevronButton
79
79
  }))), (!lazyLoad || isOpen) && (/*#__PURE__*/React.createElement(AnimateHeight, {
80
80
  height: isOpen ? 'auto' : 0,
81
+ disableDisplayNone: true,
81
82
  "data-testid": "collapsible-section-".concat(id)
82
83
  }, /*#__PURE__*/React.createElement("div", {
83
84
  id: sectionId,
84
85
  className: classnames(modules_23a5f9ec.section, noSectionPadding && modules_23a5f9ec.noPadding),
85
86
  role: "region",
86
- "aria-labelledby": buttonId
87
+ "aria-labelledby": buttonId,
88
+ // TODO: Remove @ts-expect-error when upgrade to @types/react@19 that support the `inert` HTML attribute
89
+ // @ts-expect-error current react types don't yet support the `inert` HTML attribute
90
+ inert: isOpen ? undefined : true
87
91
  }, children))));
88
92
  };
89
93
  Collapsible.displayName = 'Collapsible';
@@ -24,6 +24,7 @@ const ErrorPage = /*#__PURE__*/function () {
24
24
  title = _a.title,
25
25
  message = _a.message,
26
26
  callToAction = _a.callToAction,
27
+ tag = _a.tag,
27
28
  classNameOverride = _a.classNameOverride;
28
29
  var formatMessage = useIntl().formatMessage;
29
30
  var content = useErrorMessages(code);
@@ -40,6 +41,7 @@ const ErrorPage = /*#__PURE__*/function () {
40
41
  return /*#__PURE__*/React.createElement("div", {
41
42
  className: classnames(classNameOverride)
42
43
  }, /*#__PURE__*/React.createElement(BrandMoment, {
44
+ tag: tag,
43
45
  header: /*#__PURE__*/React.createElement(React.Fragment, null),
44
46
  body: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
45
47
  className: modules_bbf9c78c.paragraphPadding
@@ -1,4 +1,4 @@
1
- import { type HTMLAttributes, type ReactElement, type ReactNode } from 'react';
1
+ import { type ElementType, type HTMLAttributes, type ReactElement, type ReactNode } from 'react';
2
2
  import { type ButtonProps } from "../ButtonV1";
3
3
  import { type SceneProps } from "../Illustration";
4
4
  import { type OverrideClassName } from "../types/OverrideClassName";
@@ -11,6 +11,7 @@ export type BrandMomentProps = {
11
11
  body?: ReactNode;
12
12
  primaryAction?: ButtonProps;
13
13
  secondaryAction?: ButtonProps;
14
+ tag?: ElementType;
14
15
  text: {
15
16
  title: ReactNode;
16
17
  subtitle?: ReactNode;
@@ -23,7 +24,7 @@ export type BrandMomentProps = {
23
24
  * {@link https://cultureamp.design/?path=/docs/components-brand-moment--docs Storybook}
24
25
  */
25
26
  export declare const BrandMoment: {
26
- ({ variant, illustration, header, body, primaryAction, secondaryAction, text, classNameOverride, ...restProps }: BrandMomentProps): JSX.Element;
27
+ ({ variant, illustration, header, body, primaryAction, secondaryAction, tag, text, classNameOverride, ...restProps }: BrandMomentProps): JSX.Element;
27
28
  displayName: string;
28
29
  };
29
30
  export {};
@@ -1,4 +1,4 @@
1
- import React, { type HTMLAttributes } from 'react';
1
+ import React, { type ElementType, type HTMLAttributes } from 'react';
2
2
  import { type OverrideClassName } from "../types/OverrideClassName";
3
3
  import { type ErrorStatuses } from './hooks';
4
4
  export type ErrorPageProps = {
@@ -9,8 +9,9 @@ export type ErrorPageProps = {
9
9
  onContactSupport: () => void;
10
10
  homeHref?: string;
11
11
  };
12
+ tag?: ElementType;
12
13
  } & OverrideClassName<HTMLAttributes<HTMLDivElement>>;
13
14
  export declare const ErrorPage: {
14
- ({ code, title, message, callToAction, classNameOverride, }: ErrorPageProps): JSX.Element;
15
+ ({ code, title, message, callToAction, tag, classNameOverride, }: ErrorPageProps): JSX.Element;
15
16
  displayName: string;
16
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "3.0.14",
3
+ "version": "3.1.0",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -0,0 +1,44 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { vi } from 'vitest'
4
+ import { BrandMoment } from './BrandMoment'
5
+
6
+ describe('<BrandMoment />', () => {
7
+ beforeAll(() => {
8
+ window.matchMedia = vi.fn().mockImplementation(() => ({
9
+ media: '',
10
+ onchange: null,
11
+ addListener: vi.fn(),
12
+ removeListener: vi.fn(),
13
+ addEventListener: vi.fn(),
14
+ removeEventListener: vi.fn(),
15
+ matches: false,
16
+ }))
17
+ })
18
+
19
+ it('renders a main element by default', () => {
20
+ const { container } = render(
21
+ <BrandMoment
22
+ variant="informative"
23
+ illustration={<div>illustration</div>}
24
+ header={<div>header</div>}
25
+ text={{ title: 'Title' }}
26
+ />,
27
+ )
28
+ expect(container.querySelector('main')).toBeInTheDocument()
29
+ })
30
+
31
+ it('renders the specified tag when provided', () => {
32
+ const { container } = render(
33
+ <BrandMoment
34
+ variant="informative"
35
+ illustration={<div>illustration</div>}
36
+ header={<div>header</div>}
37
+ tag="section"
38
+ text={{ title: 'Title' }}
39
+ />,
40
+ )
41
+ expect(container.querySelector('section')).toBeInTheDocument()
42
+ expect(container.querySelector('main')).not.toBeInTheDocument()
43
+ })
44
+ })
@@ -1,4 +1,9 @@
1
- import React, { type HTMLAttributes, type ReactElement, type ReactNode } from 'react'
1
+ import React, {
2
+ type ElementType,
3
+ type HTMLAttributes,
4
+ type ReactElement,
5
+ type ReactNode,
6
+ } from 'react'
2
7
  import classnames from 'classnames'
3
8
  import { Button, type ButtonProps } from '~components/ButtonV1'
4
9
  import { Heading } from '~components/Heading'
@@ -19,6 +24,7 @@ export type BrandMomentProps = {
19
24
  body?: ReactNode
20
25
  primaryAction?: ButtonProps
21
26
  secondaryAction?: ButtonProps
27
+ tag?: ElementType
22
28
  text: {
23
29
  title: ReactNode
24
30
  subtitle?: ReactNode
@@ -39,16 +45,18 @@ export const BrandMoment = ({
39
45
  body,
40
46
  primaryAction,
41
47
  secondaryAction,
48
+ tag,
42
49
  text,
43
50
  classNameOverride,
44
51
  ...restProps
45
52
  }: BrandMomentProps): JSX.Element => {
46
53
  const { queries } = useMediaQueries()
54
+ const Tag = tag ?? 'main'
47
55
 
48
56
  return (
49
57
  <div className={classnames(styles.body, styles[variant], classNameOverride)} {...restProps}>
50
58
  <header className={styles.header}>{header}</header>
51
- <main className={styles.main}>
59
+ <Tag className={styles.main}>
52
60
  <div className={styles.container}>
53
61
  <div className={styles.mainInner}>
54
62
  <div className={styles.left}>
@@ -88,7 +96,7 @@ export const BrandMoment = ({
88
96
  </div>
89
97
  </div>
90
98
  </div>
91
- </main>
99
+ </Tag>
92
100
  {text.footer && (
93
101
  <footer className={styles.footer}>
94
102
  <div className={styles.container}>
@@ -94,4 +94,47 @@ describe('<Collapsible />', () => {
94
94
  expect(section.style.height).toEqual('auto')
95
95
  })
96
96
  })
97
+
98
+ it('sets inert on the section when closed by default', async () => {
99
+ const { container } = render(
100
+ <Collapsible id="1" title="First panel" controlled>
101
+ First panel content
102
+ </Collapsible>,
103
+ )
104
+ const section = container.querySelector('[id="1-section"]')
105
+ expect(section).toHaveAttribute('inert')
106
+ })
107
+
108
+ it('does not set inert on the section when open by default', async () => {
109
+ const { container } = render(
110
+ <Collapsible id="1" title="First panel" open>
111
+ First panel content
112
+ </Collapsible>,
113
+ )
114
+ const section = container.querySelector('[id="1-section"]')
115
+ expect(section).not.toHaveAttribute('inert')
116
+ })
117
+
118
+ it('removes inert when opened and re-applies it when closed', async () => {
119
+ const { container, getByTestId } = render(
120
+ <Collapsible id="1" title="First panel">
121
+ First panel content
122
+ </Collapsible>,
123
+ )
124
+
125
+ const section = container.querySelector('[id="1-section"]')
126
+ const header = getByTestId('collapsible-header-1')
127
+
128
+ expect(section).toHaveAttribute('inert')
129
+
130
+ await user.click(header)
131
+ await waitFor(() => {
132
+ expect(section).not.toHaveAttribute('inert')
133
+ })
134
+
135
+ await user.click(header)
136
+ await waitFor(() => {
137
+ expect(section).toHaveAttribute('inert')
138
+ })
139
+ })
97
140
  })
@@ -128,12 +128,19 @@ export const Collapsible = ({
128
128
  </div>
129
129
  </div>
130
130
  {(!lazyLoad || isOpen) && (
131
- <AnimateHeight height={isOpen ? 'auto' : 0} data-testid={`collapsible-section-${id}`}>
131
+ <AnimateHeight
132
+ height={isOpen ? 'auto' : 0}
133
+ disableDisplayNone
134
+ data-testid={`collapsible-section-${id}`}
135
+ >
132
136
  <div
133
137
  id={sectionId}
134
138
  className={classnames(styles.section, noSectionPadding && styles.noPadding)}
135
139
  role="region"
136
140
  aria-labelledby={buttonId}
141
+ // TODO: Remove @ts-expect-error when upgrade to @types/react@19 that support the `inert` HTML attribute
142
+ // @ts-expect-error current react types don't yet support the `inert` HTML attribute
143
+ inert={isOpen ? undefined : true}
137
144
  >
138
145
  {children}
139
146
  </div>
@@ -2,6 +2,7 @@ import React, { useState } from 'react'
2
2
  import { type Meta, type StoryObj } from '@storybook/react'
3
3
  import { Heading } from '~components/Heading'
4
4
  import { Icon } from '~components/Icon'
5
+ import { SingleSelect } from '~components/SingleSelect'
5
6
  import { Text } from '~components/Text'
6
7
  import { Collapsible } from '../index'
7
8
 
@@ -100,3 +101,33 @@ export const Controlled: Story = {
100
101
  },
101
102
  parameters: { docs: { source: { code: controlledSourceCode } } },
102
103
  }
104
+
105
+ export const WithSingleSelect: Story = {
106
+ args: {
107
+ title: 'Single Collapsible',
108
+ },
109
+ render: (args) => (
110
+ <Collapsible {...args}>
111
+ <Text variant="body">Content before the SingleSelect</Text>
112
+ <SingleSelect
113
+ label="Type"
114
+ items={[
115
+ { label: 'Option 1', value: 'option-1' },
116
+ { label: 'Option 2', value: 'option-2' },
117
+ { label: 'Option 3', value: 'option-3' },
118
+ ]}
119
+ />
120
+ <Text variant="body">
121
+ Content after the SingleSelect — this should be reachable via VoiceOver
122
+ </Text>
123
+ </Collapsible>
124
+ ),
125
+ parameters: {
126
+ backgrounds: { default: 'Gray 100' },
127
+ docs: {
128
+ canvas: {
129
+ sourceState: 'shown',
130
+ },
131
+ },
132
+ },
133
+ }
@@ -127,4 +127,15 @@ describe('<ErrorPage />', () => {
127
127
  expect(screen.getByText('Error code 504')).toBeVisible()
128
128
  })
129
129
  })
130
+
131
+ it('renders a main element by default', () => {
132
+ const { container } = render(<ErrorPage code="404" />)
133
+ expect(container.querySelector('main')).toBeInTheDocument()
134
+ })
135
+
136
+ it('passes tag prop to BrandMoment', () => {
137
+ const { container } = render(<ErrorPage code="404" tag="section" />)
138
+ expect(container.querySelector('section')).toBeInTheDocument()
139
+ expect(container.querySelector('main')).not.toBeInTheDocument()
140
+ })
130
141
  })
@@ -1,4 +1,4 @@
1
- import React, { type HTMLAttributes } from 'react'
1
+ import React, { type ElementType, type HTMLAttributes } from 'react'
2
2
  import { FormattedMessage, useIntl } from '@cultureamp/i18n-react-intl'
3
3
  import classNames from 'classnames'
4
4
  import { BrandMoment } from '~components/BrandMoment'
@@ -26,6 +26,7 @@ export type ErrorPageProps = {
26
26
  onContactSupport: () => void
27
27
  homeHref?: string
28
28
  }
29
+ tag?: ElementType
29
30
  } & OverrideClassName<HTMLAttributes<HTMLDivElement>>
30
31
 
31
32
  export const ErrorPage = ({
@@ -33,6 +34,7 @@ export const ErrorPage = ({
33
34
  title,
34
35
  message,
35
36
  callToAction,
37
+ tag,
36
38
  classNameOverride,
37
39
  }: ErrorPageProps): JSX.Element => {
38
40
  const { formatMessage } = useIntl()
@@ -48,6 +50,7 @@ export const ErrorPage = ({
48
50
  return (
49
51
  <div className={classNames(classNameOverride)}>
50
52
  <BrandMoment
53
+ tag={tag}
51
54
  header={<></>}
52
55
  body={
53
56
  <>