@atlassian/clientside-extensions-components 4.0.2 → 5.1.0-5a180d69-mdyn784a
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.
- package/dist/cjs/ExtensionPoint.js +1 -1
- package/dist/cjs/ExtensionPoint.js.map +1 -1
- package/dist/cjs/ExtensionPointInfo.js +27 -20
- package/dist/cjs/ExtensionPointInfo.js.map +1 -1
- package/dist/cjs/ExtensionsObservable.js +1 -1
- package/dist/cjs/ExtensionsObservable.js.map +1 -1
- package/dist/cjs/handlers/AsyncPanelHandler.js +1 -1
- package/dist/cjs/handlers/AsyncPanelHandler.js.map +1 -1
- package/dist/cjs/handlers/ButtonHandler.js +2 -2
- package/dist/cjs/handlers/ButtonHandler.js.map +1 -1
- package/dist/cjs/handlers/LinkHandler.js +2 -2
- package/dist/cjs/handlers/LinkHandler.js.map +1 -1
- package/dist/cjs/handlers/ModalHandler.js +5 -5
- package/dist/cjs/handlers/ModalHandler.js.map +1 -1
- package/dist/cjs/handlers/PanelHandler.js +1 -1
- package/dist/cjs/handlers/PanelHandler.js.map +1 -1
- package/dist/cjs/handlers/SectionHandler.js +1 -2
- package/dist/cjs/handlers/SectionHandler.js.map +1 -1
- package/dist/cjs/index.js +10 -10
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/renderElementAsReact.js +4 -3
- package/dist/cjs/renderElementAsReact.js.map +1 -1
- package/dist/cjs/styled.js +17 -47
- package/dist/cjs/styled.js.map +1 -1
- package/dist/cjs/types.js +1 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/useExtensions.js +1 -1
- package/dist/cjs/useExtensions.js.map +1 -1
- package/dist/cjs/useWebPanelRenderer.js.map +1 -1
- package/dist/esm/ExtensionPoint.js.map +1 -1
- package/dist/esm/ExtensionPointInfo.js +26 -19
- package/dist/esm/ExtensionPointInfo.js.map +1 -1
- package/dist/esm/ExtensionsObservable.js.map +1 -1
- package/dist/esm/handlers/AsyncPanelHandler.js.map +1 -1
- package/dist/esm/handlers/ButtonHandler.js.map +1 -1
- package/dist/esm/handlers/LinkHandler.js.map +1 -1
- package/dist/esm/handlers/ModalHandler.js +5 -5
- package/dist/esm/handlers/ModalHandler.js.map +1 -1
- package/dist/esm/handlers/PanelHandler.js.map +1 -1
- package/dist/esm/handlers/SectionHandler.js +0 -1
- package/dist/esm/handlers/SectionHandler.js.map +1 -1
- package/dist/esm/renderElementAsReact.js +3 -2
- package/dist/esm/renderElementAsReact.js.map +1 -1
- package/dist/esm/styled.js +14 -44
- package/dist/esm/styled.js.map +1 -1
- package/dist/esm/useExtensions.js.map +1 -1
- package/dist/esm/useWebPanelRenderer.js.map +1 -1
- package/dist/types/ExtensionPointInfo.d.ts +1 -1
- package/dist/types/ExtensionPointInfo.d.ts.map +1 -1
- package/dist/types/ExtensionsObservable.d.ts.map +1 -1
- package/dist/types/debug/types.d.ts +1 -1
- package/dist/types/debug/types.d.ts.map +1 -1
- package/dist/types/debug/useDebug.d.ts.map +1 -1
- package/dist/types/debug/useDiscovery.d.ts.map +1 -1
- package/dist/types/debug/useLogging.d.ts.map +1 -1
- package/dist/types/debug/useValidation.d.ts.map +1 -1
- package/dist/types/handlers/AsyncPanelHandler.d.ts +1 -1
- package/dist/types/handlers/AsyncPanelHandler.d.ts.map +1 -1
- package/dist/types/handlers/ButtonHandler.d.ts +2 -0
- package/dist/types/handlers/ButtonHandler.d.ts.map +1 -1
- package/dist/types/handlers/LinkHandler.d.ts +2 -0
- package/dist/types/handlers/LinkHandler.d.ts.map +1 -1
- package/dist/types/handlers/ModalHandler.d.ts +2 -1
- package/dist/types/handlers/ModalHandler.d.ts.map +1 -1
- package/dist/types/handlers/SectionHandler.d.ts +6 -1
- package/dist/types/handlers/SectionHandler.d.ts.map +1 -1
- package/dist/types/renderElementAsReact.d.ts +2 -1
- package/dist/types/renderElementAsReact.d.ts.map +1 -1
- package/dist/types/styled.d.ts +33 -14
- package/dist/types/styled.d.ts.map +1 -1
- package/dist/types/types.d.ts +4 -4
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/useExtensions.d.ts.map +1 -1
- package/lib/ExtensionPoint.test.tsx +1 -1
- package/lib/ExtensionPointInfo.test.tsx +1 -1
- package/lib/ExtensionPointInfo.tsx +51 -24
- package/lib/ExtensionsObservable.test.ts +1 -1
- package/lib/__snapshots__/ExtensionPointInfo.test.tsx.snap +203 -124
- package/lib/debug/useDebug.test.ts +2 -2
- package/lib/debug/useDiscovery.test.ts +2 -2
- package/lib/debug/useLogging.test.ts +2 -2
- package/lib/debug/useValidation.test.ts +2 -2
- package/lib/handlers/ButtonHandler.test.tsx +5 -1
- package/lib/handlers/ButtonHandler.tsx +3 -1
- package/lib/handlers/LinkHandler.test.tsx +5 -1
- package/lib/handlers/LinkHandler.tsx +1 -0
- package/lib/handlers/ModalHandler.test.tsx +9 -7
- package/lib/handlers/ModalHandler.tsx +11 -9
- package/lib/handlers/SectionHandler.test.tsx +1 -1
- package/lib/handlers/SectionHandler.tsx +5 -1
- package/lib/renderElementAsReact.test.tsx +7 -6
- package/lib/renderElementAsReact.tsx +12 -3
- package/lib/styled.ts +15 -46
- package/lib/useExtensions.test.ts +49 -43
- package/package.json +25 -24
|
@@ -6,7 +6,11 @@ import type { FunctionComponent } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { LinkHandler } from './LinkHandler';
|
|
8
8
|
|
|
9
|
-
const TestComponent: FunctionComponent<{ url: string; attributes?: object }> = ({
|
|
9
|
+
const TestComponent: FunctionComponent<{ url: string; attributes?: object; children?: React.ReactNode }> = ({
|
|
10
|
+
url,
|
|
11
|
+
attributes,
|
|
12
|
+
children,
|
|
13
|
+
}) => {
|
|
10
14
|
return (
|
|
11
15
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
12
16
|
<LinkHandler href={url} {...attributes}>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable jest/no-commented-out-tests */
|
|
2
2
|
/* eslint-disable react/jsx-boolean-value */
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { render, fireEvent } from '@testing-library/react';
|
|
4
|
+
import { render, fireEvent, waitFor } from '@testing-library/react';
|
|
5
5
|
|
|
6
6
|
import type { ModalExtension } from '@atlassian/clientside-extensions';
|
|
7
7
|
import type { FunctionComponent } from 'react';
|
|
@@ -53,14 +53,15 @@ describe('ModalHandler', () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
describe('ModalHandler', () => {
|
|
56
|
-
it('should receive an isOpen prop to set the modal as open', () => {
|
|
56
|
+
it('should receive an isOpen prop to set the modal as open', async () => {
|
|
57
57
|
const { rerender, queryByRole } = render(<TestComponent isOpen={false} />);
|
|
58
58
|
|
|
59
59
|
expect(queryByRole('dialog')).toBeFalsy();
|
|
60
60
|
|
|
61
61
|
rerender(<TestComponent isOpen={true} />);
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
await waitFor(() => {
|
|
63
|
+
expect(queryByRole('dialog')).toBeTruthy();
|
|
64
|
+
});
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
it('should provide a closeModal API method to extension that executes a provided onClose prop to close the modal', () => {
|
|
@@ -262,14 +263,15 @@ describe('ModalHandler', () => {
|
|
|
262
263
|
expect(queryByText(modalButtonText)).toBeTruthy();
|
|
263
264
|
});
|
|
264
265
|
|
|
265
|
-
it('should receive an isOpen prop to set the modal as open', () => {
|
|
266
|
+
it('should receive an isOpen prop to set the modal as open', async () => {
|
|
266
267
|
const { queryByRole, queryByText } = render(<TestComponentWithActionHandler>{modalButtonText}</TestComponentWithActionHandler>);
|
|
267
268
|
|
|
268
269
|
expect(queryByRole('dialog')).toBeFalsy();
|
|
269
270
|
|
|
270
271
|
queryByText(modalButtonText)?.click();
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
await waitFor(() => {
|
|
273
|
+
expect(queryByRole('dialog')).toBeTruthy();
|
|
274
|
+
});
|
|
273
275
|
});
|
|
274
276
|
});
|
|
275
277
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import ModalDialog, {
|
|
4
|
-
import LoadingButton from '@atlaskit/button/loading-button';
|
|
1
|
+
import Button from '@atlaskit/button/new';
|
|
2
|
+
import type { KeyboardOrMouseEvent } from '@atlaskit/modal-dialog';
|
|
3
|
+
import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
|
|
5
4
|
import { ModalExtension } from '@atlassian/clientside-extensions';
|
|
6
|
-
import type {
|
|
5
|
+
import type { FunctionComponent, ReactNode } from 'react';
|
|
6
|
+
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import { ButtonHandler } from './ButtonHandler';
|
|
8
|
+
import type { OnClickHandler } from './types';
|
|
8
9
|
|
|
9
10
|
const ModalInnerBody: FunctionComponent<{ modalApi: ModalExtension.Api }> = ({ modalApi }) => {
|
|
10
11
|
const ref = useRef<HTMLDivElement>(null);
|
|
@@ -56,7 +57,7 @@ export const ModalHandler: FunctionComponent<ModalHandlerProps> = ({
|
|
|
56
57
|
const stackIndex = __disableFocusLock ? 1 : 0;
|
|
57
58
|
|
|
58
59
|
const onCloseHandler = useCallback(
|
|
59
|
-
(e) => {
|
|
60
|
+
(e: KeyboardOrMouseEvent) => {
|
|
60
61
|
const closeResult = modalApi.getOnCloseCallback()(e);
|
|
61
62
|
// prevent closing of modal if "closeRequest" is "false"
|
|
62
63
|
if (closeResult !== false) {
|
|
@@ -70,9 +71,9 @@ export const ModalHandler: FunctionComponent<ModalHandlerProps> = ({
|
|
|
70
71
|
() =>
|
|
71
72
|
actions.map(({ testId, isLoading, text, onClick, isDisabled }, index) => (
|
|
72
73
|
// eslint-disable-next-line react/no-array-index-key
|
|
73
|
-
<
|
|
74
|
+
<Button key={index} isLoading={isLoading} testId={testId} isDisabled={isDisabled} onClick={onClick}>
|
|
74
75
|
{text}
|
|
75
|
-
</
|
|
76
|
+
</Button>
|
|
76
77
|
)),
|
|
77
78
|
[actions],
|
|
78
79
|
);
|
|
@@ -81,7 +82,7 @@ export const ModalHandler: FunctionComponent<ModalHandlerProps> = ({
|
|
|
81
82
|
<Wrapper>
|
|
82
83
|
{isOpen && (
|
|
83
84
|
<ModalDialog testId="client-extensions-modal" width={modalApi.getWidth()} onClose={onCloseHandler} stackIndex={stackIndex}>
|
|
84
|
-
<ModalHeader>
|
|
85
|
+
<ModalHeader hasCloseButton>
|
|
85
86
|
<ModalTitle appearance={modalApi.getAppearance()}>{modalApi.getTitle()}</ModalTitle>
|
|
86
87
|
</ModalHeader>
|
|
87
88
|
<ModalBody>
|
|
@@ -95,6 +96,7 @@ export const ModalHandler: FunctionComponent<ModalHandlerProps> = ({
|
|
|
95
96
|
};
|
|
96
97
|
|
|
97
98
|
export interface ModalWithActionHandlerProps extends Omit<OnClickHandler, 'onAction'> {
|
|
99
|
+
children?: ReactNode;
|
|
98
100
|
render: ModalExtension.ModalRenderExtension;
|
|
99
101
|
__withoutTransition?: boolean;
|
|
100
102
|
__disableFocusLock?: boolean;
|
|
@@ -4,7 +4,7 @@ import { render } from '@testing-library/react';
|
|
|
4
4
|
|
|
5
5
|
import { SectionHandler } from './SectionHandler';
|
|
6
6
|
|
|
7
|
-
const TestComponent: FunctionComponent = ({ children }) => {
|
|
7
|
+
const TestComponent: FunctionComponent<{ children: React.ReactNode }> = ({ children }) => {
|
|
8
8
|
return <SectionHandler>{children}</SectionHandler>;
|
|
9
9
|
};
|
|
10
10
|
|
|
@@ -2,6 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import type { FunctionComponent } from 'react';
|
|
3
3
|
|
|
4
4
|
// eslint-disable-next-line import/prefer-default-export
|
|
5
|
-
|
|
5
|
+
interface SectionHandlerProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SectionHandler: FunctionComponent<SectionHandlerProps> = ({ children }) => {
|
|
6
10
|
return <>{children}</>;
|
|
7
11
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { queryByTestId } from '@testing-library/dom';
|
|
2
|
+
import { queryByTestId, findByTestId } from '@testing-library/dom';
|
|
3
3
|
|
|
4
4
|
import type { MountableExtension } from '@atlassian/clientside-extensions';
|
|
5
5
|
import renderElementAsReact from './renderElementAsReact';
|
|
@@ -8,6 +8,7 @@ import renderElementAsReact from './renderElementAsReact';
|
|
|
8
8
|
interface TestProps {
|
|
9
9
|
name?: string;
|
|
10
10
|
value?: number;
|
|
11
|
+
children?: React.ReactNode;
|
|
11
12
|
}
|
|
12
13
|
/* eslint-enable react/no-unused-prop-types */
|
|
13
14
|
|
|
@@ -27,7 +28,7 @@ describe('renderElementAsReact', () => {
|
|
|
27
28
|
},
|
|
28
29
|
};
|
|
29
30
|
|
|
30
|
-
const TestComponent
|
|
31
|
+
const TestComponent = (props: unknown) => (
|
|
31
32
|
<div data-testid="test-component">
|
|
32
33
|
<h2>A test content</h2>
|
|
33
34
|
<p data-testid="test-props">{JSON.stringify(props)}</p>
|
|
@@ -39,7 +40,7 @@ describe('renderElementAsReact', () => {
|
|
|
39
40
|
});
|
|
40
41
|
|
|
41
42
|
it('should use the renderApi to mount and unmount the given react component', () => {
|
|
42
|
-
const getTestComponent = () => queryByTestId(testContainer, 'test-component')
|
|
43
|
+
const getTestComponent = () => queryByTestId(testContainer, 'test-component');
|
|
43
44
|
|
|
44
45
|
renderElementAsReact(testRenderAPI, TestComponent);
|
|
45
46
|
|
|
@@ -50,7 +51,7 @@ describe('renderElementAsReact', () => {
|
|
|
50
51
|
|
|
51
52
|
// rendered using onMount
|
|
52
53
|
expect(getTestComponent()).toBeTruthy();
|
|
53
|
-
expect(getTestComponent()
|
|
54
|
+
expect(getTestComponent()?.textContent).toMatch('A test content');
|
|
54
55
|
|
|
55
56
|
onUnmountCallback(testContainer);
|
|
56
57
|
|
|
@@ -58,7 +59,7 @@ describe('renderElementAsReact', () => {
|
|
|
58
59
|
expect(getTestComponent()).toBeFalsy();
|
|
59
60
|
});
|
|
60
61
|
|
|
61
|
-
it('should receive additional props to assing to the given react component', () => {
|
|
62
|
+
it('should receive additional props to assing to the given react component', async () => {
|
|
62
63
|
const testProps: TestProps = {
|
|
63
64
|
name: 'Meaning of life',
|
|
64
65
|
value: 42,
|
|
@@ -67,7 +68,7 @@ describe('renderElementAsReact', () => {
|
|
|
67
68
|
renderElementAsReact(testRenderAPI, TestComponent, testProps);
|
|
68
69
|
onMountCallback(testContainer);
|
|
69
70
|
|
|
70
|
-
const testPropsElement =
|
|
71
|
+
const testPropsElement = await findByTestId(testContainer, 'test-props');
|
|
71
72
|
|
|
72
73
|
// props applied to the component when rendering
|
|
73
74
|
expect(testPropsElement).toBeTruthy();
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import ReactDOM from 'react-dom';
|
|
2
2
|
import type { ComponentType } from 'react';
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { StrictMode } from 'react';
|
|
4
4
|
import type { MountableExtension } from '@atlassian/clientside-extensions';
|
|
5
5
|
|
|
6
|
-
function renderElementAsReact<PropsT>(
|
|
6
|
+
function renderElementAsReact<PropsT extends React.JSX.IntrinsicAttributes>(
|
|
7
|
+
renderApi: MountableExtension,
|
|
8
|
+
RenderElement: ComponentType<PropsT>,
|
|
9
|
+
additionalProps?: unknown,
|
|
10
|
+
) {
|
|
7
11
|
renderApi
|
|
8
12
|
.onMount((element) => {
|
|
9
13
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
10
|
-
ReactDOM.render(
|
|
14
|
+
ReactDOM.render(
|
|
15
|
+
<StrictMode>
|
|
16
|
+
<RenderElement {...(additionalProps as PropsT)} />
|
|
17
|
+
</StrictMode>,
|
|
18
|
+
element,
|
|
19
|
+
);
|
|
11
20
|
})
|
|
12
21
|
.onUnmount((element) => {
|
|
13
22
|
ReactDOM.unmountComponentAtNode(element);
|
package/lib/styled.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { token } from '@atlaskit/tokens';
|
|
1
2
|
import styled from '@emotion/styled';
|
|
2
|
-
import { keyframes } from '@emotion/
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { gridSize } from '@atlaskit/theme/constants';
|
|
3
|
+
import { keyframes } from '@emotion/react';
|
|
4
|
+
|
|
5
|
+
export const GRID_SIZE = 8;
|
|
6
6
|
|
|
7
7
|
export const BlinkerContainer = styled.span`
|
|
8
8
|
position: relative;
|
|
@@ -18,10 +18,11 @@ const blinkAnimation = keyframes`
|
|
|
18
18
|
}
|
|
19
19
|
`;
|
|
20
20
|
|
|
21
|
+
/* eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage */
|
|
21
22
|
export const Blinker = styled.button`
|
|
22
23
|
height: 22px;
|
|
23
24
|
width: 22px;
|
|
24
|
-
background:
|
|
25
|
+
background: ${token('color.background.input')};
|
|
25
26
|
border-radius: 50%;
|
|
26
27
|
animation: ${blinkAnimation} 1s ease-in-out infinite;
|
|
27
28
|
animation-direction: alternate;
|
|
@@ -45,72 +46,40 @@ export const Blinker = styled.button`
|
|
|
45
46
|
// AK doesn't provide a plain Table styled component.
|
|
46
47
|
// Using AK theme to be compatible with their dark mode.
|
|
47
48
|
// Styles copied from: https://bitbucket.org/atlassian/atlassian-frontend/src/master/packages/design-system/dynamic-table/src/styled/
|
|
48
|
-
const rowTheme = {
|
|
49
|
-
focusOutline: themed({ light: colors.B100, dark: colors.B100 }),
|
|
50
|
-
borderColor: themed({ light: colors.N40, dark: colors.DN50 }),
|
|
51
|
-
highlightedBackground: themed({ light: colors.N20, dark: colors.DN50 }),
|
|
52
|
-
hoverBackground: themed({ light: colors.N10, dark: colors.DN40 }),
|
|
53
|
-
hoverHighlightedBackground: themed({ light: colors.N30, dark: colors.DN60 }),
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const headTheme = {
|
|
57
|
-
focusOutline: themed({ light: colors.B100, dark: colors.B100 }),
|
|
58
|
-
borderColor: themed({ light: colors.N40, dark: colors.DN50 }),
|
|
59
|
-
textColor: themed({ light: colors.N300, dark: colors.DN300 }),
|
|
60
|
-
};
|
|
61
|
-
|
|
62
49
|
export const Table = styled.table`
|
|
63
50
|
border-collapse: collapse;
|
|
64
51
|
width: 100%;
|
|
65
52
|
`;
|
|
66
53
|
|
|
67
54
|
export const HeadRow = styled.tr`
|
|
68
|
-
border-bottom:
|
|
55
|
+
border-bottom: 1px solid ${token('color.border')};
|
|
69
56
|
`;
|
|
70
57
|
|
|
71
58
|
export const HeadCell = styled.th`
|
|
72
|
-
padding: ${
|
|
73
|
-
&:first-of-type {
|
|
74
|
-
padding-left: 0;
|
|
75
|
-
}
|
|
76
|
-
&:last-of-type {
|
|
77
|
-
padding-right: 0;
|
|
78
|
-
}
|
|
59
|
+
padding: ${GRID_SIZE / 2}px ${GRID_SIZE}px;
|
|
79
60
|
border: none;
|
|
80
|
-
color: ${
|
|
61
|
+
color: ${token('color.text')};
|
|
81
62
|
box-sizing: border-box;
|
|
82
63
|
font-size: 12px;
|
|
83
64
|
font-weight: bold;
|
|
84
65
|
position: relative;
|
|
85
66
|
text-align: left;
|
|
86
67
|
vertical-align: top;
|
|
87
|
-
&:focus {
|
|
88
|
-
outline: solid 2px ${headTheme.focusOutline};
|
|
89
|
-
}
|
|
90
68
|
`;
|
|
91
69
|
|
|
92
|
-
const outlineWidth = '2px';
|
|
93
|
-
|
|
94
70
|
export const TableRow = styled.tr`
|
|
95
|
-
border-bottom:
|
|
71
|
+
border-bottom: 1px solid ${token('color.border')};
|
|
96
72
|
&:hover {
|
|
97
|
-
background-color: ${
|
|
98
|
-
}
|
|
99
|
-
&:focus {
|
|
100
|
-
outline: ${outlineWidth} solid ${rowTheme.focusOutline};
|
|
101
|
-
outline-offset: -${outlineWidth};
|
|
73
|
+
background-color: ${token('color.background.neutral.subtle.hovered')};
|
|
102
74
|
}
|
|
103
75
|
`;
|
|
104
76
|
|
|
105
77
|
export const TableCell = styled.td`
|
|
106
78
|
border: none;
|
|
107
|
-
padding: ${
|
|
79
|
+
padding: ${GRID_SIZE / 1.2}px ${GRID_SIZE}px;
|
|
108
80
|
text-align: left;
|
|
81
|
+
`;
|
|
109
82
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
&:last-of-type {
|
|
114
|
-
padding-right: 0;
|
|
115
|
-
}
|
|
83
|
+
export const HeadingWrapper = styled.div`
|
|
84
|
+
margin-top: 30px;
|
|
116
85
|
`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mocked } from '
|
|
2
|
-
import { act, renderHook } from '@testing-library/react
|
|
1
|
+
import { mocked } from 'jest-mock';
|
|
2
|
+
import { act, renderHook, waitFor } from '@testing-library/react';
|
|
3
3
|
import type { Context, ExtensionAPI, ExtensionAttributes, ExtensionDescriptor } from '@atlassian/clientside-extensions-registry';
|
|
4
4
|
import registry from '@atlassian/clientside-extensions-registry';
|
|
5
5
|
import * as debug from '@atlassian/clientside-extensions-debug';
|
|
@@ -27,7 +27,6 @@ type MockSubjectPayload = {
|
|
|
27
27
|
type MockLocationObserver = (payload: MockSubjectPayload) => void;
|
|
28
28
|
|
|
29
29
|
type ExtensionDescriptorWithContextInAttributes = ExtensionDescriptor<ExtensionAttributes & { context: Context<{}> }>;
|
|
30
|
-
type UseExtensionsAllReturnType = ReturnType<typeof useExtensionsAll>;
|
|
31
30
|
|
|
32
31
|
function applyAttributesProvider(descriptor: ExtensionDescriptor) {
|
|
33
32
|
const { attributesProvider, ...restDescriptor } = descriptor;
|
|
@@ -288,7 +287,7 @@ describe('useExtensionsAll hook', () => {
|
|
|
288
287
|
onAction: () => updateAttributes({ label: 'a new fake label' }),
|
|
289
288
|
}),
|
|
290
289
|
};
|
|
291
|
-
const { result
|
|
290
|
+
const { result } = renderHook(() => useExtensionsAll('fake-location', null, options));
|
|
292
291
|
|
|
293
292
|
act(() => {
|
|
294
293
|
locationObserver({ descriptors: [changeAttributesExtension], loadingState: false });
|
|
@@ -299,11 +298,12 @@ describe('useExtensionsAll hook', () => {
|
|
|
299
298
|
const [testExtension] = extensions as ExtensionDescriptor<ExtensionAttributes & { onAction: () => void }>[];
|
|
300
299
|
testExtension.attributes.onAction();
|
|
301
300
|
});
|
|
302
|
-
await waitForNextUpdate();
|
|
303
301
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
await waitFor(() => {
|
|
303
|
+
const [extensionsUpdated] = result.current;
|
|
304
|
+
const [testExtensionChanged] = extensionsUpdated;
|
|
305
|
+
expect(testExtensionChanged.attributes.label).toBe('a new fake label');
|
|
306
|
+
});
|
|
307
307
|
});
|
|
308
308
|
|
|
309
309
|
it('should catch errors when calling update attributes API', () => {
|
|
@@ -344,7 +344,8 @@ describe('useExtensionsAll hook', () => {
|
|
|
344
344
|
message: expect.stringContaining('Updating attributes for extension fake-1 failed'),
|
|
345
345
|
}),
|
|
346
346
|
);
|
|
347
|
-
|
|
347
|
+
// The hook should not throw an error, so we just verify it renders successfully
|
|
348
|
+
expect(result.current).toBeDefined();
|
|
348
349
|
});
|
|
349
350
|
|
|
350
351
|
it('should add new attributes when using `updateAttributes` function', async () => {
|
|
@@ -364,7 +365,7 @@ describe('useExtensionsAll hook', () => {
|
|
|
364
365
|
},
|
|
365
366
|
};
|
|
366
367
|
|
|
367
|
-
const { result
|
|
368
|
+
const { result } = renderHook(() => useExtensions('fake-location', null, options));
|
|
368
369
|
|
|
369
370
|
// First render
|
|
370
371
|
act(() => {
|
|
@@ -380,10 +381,11 @@ describe('useExtensionsAll hook', () => {
|
|
|
380
381
|
beforeIcon: 'my-icon',
|
|
381
382
|
}),
|
|
382
383
|
);
|
|
383
|
-
await waitForNextUpdate();
|
|
384
384
|
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
await waitFor(() => {
|
|
386
|
+
const [extensionSecondRender] = result.current;
|
|
387
|
+
expect(extensionSecondRender.attributes).toHaveProperty('beforeIcon', 'my-icon');
|
|
388
|
+
});
|
|
387
389
|
});
|
|
388
390
|
|
|
389
391
|
it('should extend the existing attributes when using `updateAttributes` function with a functional update', async () => {
|
|
@@ -411,7 +413,7 @@ describe('useExtensionsAll hook', () => {
|
|
|
411
413
|
},
|
|
412
414
|
};
|
|
413
415
|
|
|
414
|
-
const { result
|
|
416
|
+
const { result } = renderHook(() => useExtensions('fake-location', null, options));
|
|
415
417
|
|
|
416
418
|
// First render
|
|
417
419
|
act(() => {
|
|
@@ -432,12 +434,12 @@ describe('useExtensionsAll hook', () => {
|
|
|
432
434
|
}),
|
|
433
435
|
);
|
|
434
436
|
|
|
435
|
-
await
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
437
|
+
await waitFor(() => {
|
|
438
|
+
expect(result.current[0].attributes).toEqual({
|
|
439
|
+
type: 'button',
|
|
440
|
+
label: 'bar',
|
|
441
|
+
counter: 1,
|
|
442
|
+
});
|
|
441
443
|
});
|
|
442
444
|
|
|
443
445
|
// Second update with manual attributes merge
|
|
@@ -451,12 +453,12 @@ describe('useExtensionsAll hook', () => {
|
|
|
451
453
|
}),
|
|
452
454
|
);
|
|
453
455
|
|
|
454
|
-
await
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
456
|
+
await waitFor(() => {
|
|
457
|
+
expect(result.current[0].attributes).toEqual({
|
|
458
|
+
type: 'button',
|
|
459
|
+
label: 'baz',
|
|
460
|
+
counter: 2,
|
|
461
|
+
});
|
|
460
462
|
});
|
|
461
463
|
|
|
462
464
|
// Third update with auto-merge
|
|
@@ -466,12 +468,12 @@ describe('useExtensionsAll hook', () => {
|
|
|
466
468
|
})),
|
|
467
469
|
);
|
|
468
470
|
|
|
469
|
-
await
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
471
|
+
await waitFor(() => {
|
|
472
|
+
expect(result.current[0].attributes).toEqual({
|
|
473
|
+
type: 'button',
|
|
474
|
+
label: 'baz',
|
|
475
|
+
counter: 4,
|
|
476
|
+
});
|
|
475
477
|
});
|
|
476
478
|
});
|
|
477
479
|
|
|
@@ -599,9 +601,9 @@ describe('useExtensionsAll hook', () => {
|
|
|
599
601
|
});
|
|
600
602
|
|
|
601
603
|
it('should throw an error if no context schema is provided when providing context', () => {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
604
|
+
expect(() => {
|
|
605
|
+
renderHook(() => useExtensionsAll('fake-location', { info: 'test-info' }, { attributeValidator }));
|
|
606
|
+
}).toThrow('No context validator specified for extension point "fake-location"');
|
|
605
607
|
});
|
|
606
608
|
|
|
607
609
|
it('should call the attributes provider only once when passed context did not changed', () => {
|
|
@@ -621,9 +623,11 @@ describe('useExtensionsAll hook', () => {
|
|
|
621
623
|
}),
|
|
622
624
|
};
|
|
623
625
|
|
|
624
|
-
const { result, rerender } = renderHook
|
|
625
|
-
|
|
626
|
-
|
|
626
|
+
const { result, rerender } = renderHook(({ context }) => useExtensionsAll('fake-location', context, options), {
|
|
627
|
+
initialProps: {
|
|
628
|
+
context: stableContext,
|
|
629
|
+
},
|
|
630
|
+
});
|
|
627
631
|
|
|
628
632
|
// First call
|
|
629
633
|
rerender({
|
|
@@ -656,11 +660,11 @@ describe('useExtensionsAll hook', () => {
|
|
|
656
660
|
});
|
|
657
661
|
|
|
658
662
|
it('should call the attributes provider twice when passed context did changed', () => {
|
|
659
|
-
const firstContext = {
|
|
663
|
+
const firstContext: Context<{}> = {
|
|
660
664
|
foo: 'bar',
|
|
661
665
|
};
|
|
662
666
|
|
|
663
|
-
const secondContext = {
|
|
667
|
+
const secondContext: Context<{}> = {
|
|
664
668
|
biz: 'baz',
|
|
665
669
|
};
|
|
666
670
|
|
|
@@ -676,9 +680,11 @@ describe('useExtensionsAll hook', () => {
|
|
|
676
680
|
}),
|
|
677
681
|
};
|
|
678
682
|
|
|
679
|
-
const { result, rerender } = renderHook
|
|
680
|
-
|
|
681
|
-
|
|
683
|
+
const { result, rerender } = renderHook(({ context }) => useExtensionsAll('fake-location', context, options), {
|
|
684
|
+
initialProps: {
|
|
685
|
+
context: firstContext,
|
|
686
|
+
},
|
|
687
|
+
});
|
|
682
688
|
|
|
683
689
|
// First update
|
|
684
690
|
rerender({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlassian/clientside-extensions-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0-5a180d69-mdyn784a",
|
|
4
4
|
"description": "React components to assist in the authoring of extension points that work at product runtime.",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"homepage": "https://bitbucket.org/atlassian/atlassian-clientside-extensions#readme",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
],
|
|
54
54
|
"publishConfig": {
|
|
55
55
|
"access": "public",
|
|
56
|
-
"registry": "https://
|
|
56
|
+
"registry": "https://packages.atlassian.com/api/npm/npm-public/"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"prepublish": "yarn build",
|
|
@@ -66,39 +66,40 @@
|
|
|
66
66
|
"test:watch": "jest --watch"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@atlaskit/button": "
|
|
70
|
-
"@atlaskit/
|
|
71
|
-
"@atlaskit/
|
|
72
|
-
"@
|
|
73
|
-
"@atlassian/clientside-extensions
|
|
74
|
-
"@atlassian/clientside-extensions-
|
|
75
|
-
"@atlassian/clientside-extensions-
|
|
76
|
-
"@atlassian/clientside-extensions-
|
|
77
|
-
"@
|
|
78
|
-
"@emotion/
|
|
69
|
+
"@atlaskit/button": "23.3.1",
|
|
70
|
+
"@atlaskit/heading": "5.2.3",
|
|
71
|
+
"@atlaskit/modal-dialog": "14.3.2",
|
|
72
|
+
"@atlaskit/tokens": "5.6.2",
|
|
73
|
+
"@atlassian/clientside-extensions": "^5.1.0-5a180d69-mdyn784a",
|
|
74
|
+
"@atlassian/clientside-extensions-base": "^5.1.0-5a180d69-mdyn784a",
|
|
75
|
+
"@atlassian/clientside-extensions-debug": "^5.1.0-5a180d69-mdyn784a",
|
|
76
|
+
"@atlassian/clientside-extensions-registry": "^5.1.0-5a180d69-mdyn784a",
|
|
77
|
+
"@atlassian/clientside-extensions-schema": "^5.1.0-5a180d69-mdyn784a",
|
|
78
|
+
"@emotion/react": "^11.13.5",
|
|
79
|
+
"@emotion/styled": "^11.13.5",
|
|
79
80
|
"lodash.isequal": "^4.5.0",
|
|
80
|
-
"react-scroll": "^1.8.
|
|
81
|
+
"react-scroll": "^1.8.10",
|
|
81
82
|
"tslib": "^2.2.0"
|
|
82
83
|
},
|
|
83
84
|
"peerDependencies": {
|
|
84
|
-
"react": "^
|
|
85
|
-
"react-dom": "^
|
|
85
|
+
"react": "^18.0.0",
|
|
86
|
+
"react-dom": "^18.0.0"
|
|
86
87
|
},
|
|
87
88
|
"devDependencies": {
|
|
88
89
|
"@testing-library/dom": "8.11.1",
|
|
89
90
|
"@testing-library/jest-dom": "5.15.1",
|
|
90
|
-
"@testing-library/react": "
|
|
91
|
-
"@testing-library/react-hooks": "7.0.2",
|
|
91
|
+
"@testing-library/react": "16.3.0",
|
|
92
92
|
"@types/lodash.isequal": "4.5.5",
|
|
93
|
-
"@types/react": "
|
|
94
|
-
"@types/react-scroll": "1.8.
|
|
93
|
+
"@types/react": "18.3.1",
|
|
94
|
+
"@types/react-scroll": "^1.8.10",
|
|
95
95
|
"jest": "27.3.1",
|
|
96
|
-
"
|
|
97
|
-
"react
|
|
98
|
-
"
|
|
96
|
+
"jest-mock": "30.0.5",
|
|
97
|
+
"react": "18.3.1",
|
|
98
|
+
"react-dom": "18.3.1",
|
|
99
|
+
"ts-jest": "29.4.1"
|
|
99
100
|
},
|
|
100
101
|
"engines": {
|
|
101
|
-
"node": ">=20.
|
|
102
|
+
"node": ">=20.18.1"
|
|
102
103
|
},
|
|
103
|
-
"gitHead": "
|
|
104
|
+
"gitHead": "5a180d69885a02ac1d96b980893b6a5c77e9459a"
|
|
104
105
|
}
|