Skip to content

Commit 010328c

Browse files
authored
feat: Add instance id to alert/flash runtime replacement API (#3500)
1 parent 62b757d commit 010328c

File tree

4 files changed

+38
-5
lines changed

4 files changed

+38
-5
lines changed

src/alert/__tests__/runtime-content.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,11 @@ test('removes styling if replacement is explicitly empty', () => {
171171

172172
describe('runReplacer arguments', () => {
173173
const runReplacer = jest.fn();
174+
const initialCheck = jest.fn();
174175
beforeEach(() => {
175176
const plugin: AlertFlashContentConfig = {
176177
id: 'test-content',
178+
initialCheck,
177179
runReplacer,
178180
};
179181
awsuiPlugins.alertContent.registerContentReplacer(plugin);
@@ -195,6 +197,18 @@ describe('runReplacer arguments', () => {
195197
render(<Alert type="error" />);
196198
expect(runReplacer.mock.lastCall[0].type).toBe('error');
197199
});
200+
201+
test('persists instanceId between initial check and runtime replacement', () => {
202+
const { rerender } = render(<Alert key={1}>Alert content</Alert>);
203+
const initialId = initialCheck.mock.lastCall[0].instanceId;
204+
expect(initialId).toEqual(expect.any(String));
205+
expect(initialId).toEqual(runReplacer.mock.lastCall[0].instanceId);
206+
initialCheck.mockClear();
207+
runReplacer.mockClear();
208+
rerender(<Alert key={2}>Alert content</Alert>);
209+
expect(initialId).not.toEqual(initialCheck.mock.lastCall[0].instanceId);
210+
expect(initialCheck.mock.lastCall[0].instanceId).toEqual(runReplacer.mock.lastCall[0].instanceId);
211+
});
198212
});
199213

200214
test('calls unmount callback', () => {

src/flashbar/__tests__/runtime-content.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,11 @@ test('restores content and header', async () => {
159159

160160
describe('runReplacer arguments', () => {
161161
const runReplacer = jest.fn();
162+
const initialCheck = jest.fn();
162163
beforeEach(() => {
163164
const plugin: AlertFlashContentConfig = {
164165
id: 'test-content',
166+
initialCheck,
165167
runReplacer,
166168
};
167169
awsuiPlugins.flashContent.registerContentReplacer(plugin);
@@ -189,6 +191,18 @@ describe('runReplacer arguments', () => {
189191
render(<Flashbar items={[{ type: 'error' }]} />);
190192
expect(runReplacer.mock.lastCall[0].type).toBe('error');
191193
});
194+
195+
test('persists instanceId between initial check and runtime replacement', () => {
196+
const { rerender } = render(<Flashbar key={1} items={[{ content: 'Something' }]} />);
197+
const initialId = initialCheck.mock.lastCall[0].instanceId;
198+
expect(initialId).toEqual(expect.any(String));
199+
expect(initialId).toEqual(runReplacer.mock.lastCall[0].instanceId);
200+
initialCheck.mockClear();
201+
runReplacer.mockClear();
202+
rerender(<Flashbar key={2} items={[{ content: 'Something' }]} />);
203+
expect(initialId).not.toEqual(initialCheck.mock.lastCall[0].instanceId);
204+
expect(initialCheck.mock.lastCall[0].instanceId).toEqual(runReplacer.mock.lastCall[0].instanceId);
205+
});
192206
});
193207

194208
test('calls unmount callback', () => {

src/internal/plugins/controllers/alert-flash-content.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ interface RefShim<T> {
1212
}
1313

1414
interface AlertFlashContentContext {
15+
instanceId: string;
1516
type: string;
1617
headerRef: RefShim<HTMLElement>;
1718
contentRef: RefShim<HTMLElement>;
1819
}
1920

2021
interface AlertFlashContentInitialContext {
22+
instanceId: string;
2123
type: string;
2224
header?: ReactNode;
2325
content?: ReactNode;

src/internal/plugins/helpers/use-discovered-content.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import { ReactNode, useEffect, useRef, useState } from 'react';
3+
import React, { useEffect, useRef, useState } from 'react';
44

5+
import { useUniqueId } from '../../hooks/use-unique-id';
56
import {
67
AlertFlashContentApiInternal,
78
AlertFlashContentResult,
@@ -16,15 +17,17 @@ export function createUseDiscoveredContent(componentName: string, controller: Al
1617
children,
1718
}: {
1819
type: string;
19-
header: ReactNode;
20-
children: ReactNode;
20+
header: React.ReactNode;
21+
children: React.ReactNode;
2122
}) {
23+
const instanceId = useUniqueId(`${componentName}-discovered-content`);
2224
const headerRef = useRef<HTMLDivElement>(null);
2325
const contentRef = useRef<HTMLDivElement>(null);
2426
const replacementHeaderRef = useRef<HTMLDivElement>(null);
2527
const replacementContentRef = useRef<HTMLDivElement>(null);
2628
const [initialHidden, setInitialHidden] = useState(() =>
2729
controller.initialCheck({
30+
instanceId,
2831
type,
2932
header,
3033
content: children,
@@ -35,7 +38,7 @@ export function createUseDiscoveredContent(componentName: string, controller: Al
3538
const mountedProvider = useRef<AlertFlashContentResult | undefined>();
3639

3740
useEffect(() => {
38-
const context = { type, headerRef, contentRef };
41+
const context = { instanceId, type, headerRef, contentRef };
3942

4043
setInitialHidden(false);
4144

@@ -96,7 +99,7 @@ export function createUseDiscoveredContent(componentName: string, controller: Al
9699
mounted = false;
97100
};
98101
});
99-
}, [type]);
102+
}, [instanceId, type]);
100103

101104
useEffect(() => {
102105
mountedProvider.current?.update();

0 commit comments

Comments
 (0)