Skip to content

Commit

Permalink
Fix promptinput height in split panel
Browse files Browse the repository at this point in the history
  • Loading branch information
YueyingLu committed Oct 29, 2024
1 parent a528836 commit 4a561a9
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 128 deletions.
259 changes: 137 additions & 122 deletions pages/prompt-input/simple.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import React, { useContext, useEffect, useState } from 'react';

import { Box, TokenGroup } from '~components';
import { AppLayout, Box, SplitPanel, TokenGroup } from '~components';
import ButtonGroup from '~components/button-group';
import Checkbox from '~components/checkbox';
import ColumnLayout from '~components/column-layout';
Expand Down Expand Up @@ -31,6 +31,7 @@ const placeholderText =

export default function PromptInputPage() {
const [textareaValue, setTextareaValue] = useState('');
const [valueInSplitPanel, setValueInSplitPanel] = useState('');
const { urlParams, setUrlParams } = useContext(AppContext as DemoContext);

const { isDisabled, isReadOnly, isInvalid, hasWarning, hasText, hasSecondaryActions, hasSecondaryContent } =
Expand Down Expand Up @@ -74,128 +75,142 @@ export default function PromptInputPage() {
const ref = React.createRef<HTMLTextAreaElement>();

return (
<div style={{ padding: 10 }}>
<h1>PromptInput demo</h1>
<SpaceBetween size="xl">
<FormField label="Settings">
<Checkbox checked={isDisabled} onChange={() => setUrlParams({ isDisabled: !isDisabled })}>
Disabled
</Checkbox>
<Checkbox checked={isReadOnly} onChange={() => setUrlParams({ isReadOnly: !isReadOnly })}>
Read-only
</Checkbox>
<Checkbox checked={isInvalid} onChange={() => setUrlParams({ isInvalid: !isInvalid })}>
Invalid
</Checkbox>
<Checkbox checked={hasWarning} onChange={() => setUrlParams({ hasWarning: !hasWarning })}>
Warning
</Checkbox>
<Checkbox
checked={hasSecondaryContent}
onChange={() =>
setUrlParams({
hasSecondaryContent: !hasSecondaryContent,
})
}
>
Secondary content
</Checkbox>
<Checkbox
checked={hasSecondaryActions}
onChange={() =>
setUrlParams({
hasSecondaryActions: !hasSecondaryActions,
})
}
>
Secondary actions
</Checkbox>
</FormField>
<button id="placeholder-text-button" onClick={() => setUrlParams({ hasText: true })}>
Fill with placeholder text
</button>
<AppLayout
content={
<div style={{ padding: 10 }}>
<h1>PromptInput demo</h1>
<SpaceBetween size="xl">
<FormField label="Settings">
<Checkbox checked={isDisabled} onChange={() => setUrlParams({ isDisabled: !isDisabled })}>
Disabled
</Checkbox>
<Checkbox checked={isReadOnly} onChange={() => setUrlParams({ isReadOnly: !isReadOnly })}>
Read-only
</Checkbox>
<Checkbox checked={isInvalid} onChange={() => setUrlParams({ isInvalid: !isInvalid })}>
Invalid
</Checkbox>
<Checkbox checked={hasWarning} onChange={() => setUrlParams({ hasWarning: !hasWarning })}>
Warning
</Checkbox>
<Checkbox
checked={hasSecondaryContent}
onChange={() =>
setUrlParams({
hasSecondaryContent: !hasSecondaryContent,
})
}
>
Secondary content
</Checkbox>
<Checkbox
checked={hasSecondaryActions}
onChange={() =>
setUrlParams({
hasSecondaryActions: !hasSecondaryActions,
})
}
>
Secondary actions
</Checkbox>
</FormField>
<button id="placeholder-text-button" onClick={() => setUrlParams({ hasText: true })}>
Fill with placeholder text
</button>

<button id="focus-button" onClick={() => ref.current?.focus()}>
Focus component
</button>
<button onClick={() => ref.current?.select()}>Select all text</button>
<button id="focus-button" onClick={() => ref.current?.focus()}>
Focus component
</button>
<button onClick={() => ref.current?.select()}>Select all text</button>

<ColumnLayout columns={2}>
<FormField
errorText={(textareaValue.length > MAX_CHARS || isInvalid) && 'The query has too many characters.'}
warningText={hasWarning && 'This input has a warning'}
constraintText={
<>
This service is subject to some policy. Character count: {textareaValue.length}/{MAX_CHARS}
</>
}
label={<span>User prompt</span>}
i18nStrings={{ errorIconAriaLabel: 'Error' }}
>
<PromptInput
ariaLabel="Chat input"
actionButtonIconName="send"
actionButtonAriaLabel="Submit prompt"
value={textareaValue}
onChange={(event: any) => setTextareaValue(event.detail.value)}
onAction={event => window.alert(`Submitted the following: ${event.detail.value}`)}
placeholder="Ask a question"
maxRows={4}
disabled={isDisabled}
readOnly={isReadOnly}
invalid={isInvalid || textareaValue.length > MAX_CHARS}
warning={hasWarning}
ref={ref}
disableSecondaryActionsPaddings={true}
secondaryActions={
hasSecondaryActions ? (
<Box padding={{ left: 'xxs', top: 'xs' }}>
<ButtonGroup
ariaLabel="Chat actions"
items={[
{
type: 'icon-button',
id: 'copy',
iconName: 'upload',
text: 'Upload files',
disabled: isDisabled || isReadOnly,
},
{
type: 'icon-button',
id: 'expand',
iconName: 'expand',
text: 'Go full page',
disabled: isDisabled || isReadOnly,
},
{
type: 'icon-button',
id: 'remove',
iconName: 'remove',
text: 'Remove',
disabled: isDisabled || isReadOnly,
},
]}
variant="icon"
/>
</Box>
) : undefined
}
secondaryContent={
hasSecondaryContent ? (
<TokenGroup
onDismiss={({ detail: { itemIndex } }) => {
setItems([...items.slice(0, itemIndex), ...items.slice(itemIndex + 1)]);
}}
items={items}
readOnly={isReadOnly}
/>
) : undefined
}
/>
</FormField>
<div />
</ColumnLayout>
</SpaceBetween>
</div>
<ColumnLayout columns={2}>
<FormField
errorText={(textareaValue.length > MAX_CHARS || isInvalid) && 'The query has too many characters.'}
warningText={hasWarning && 'This input has a warning'}
constraintText={
<>
This service is subject to some policy. Character count: {textareaValue.length}/{MAX_CHARS}
</>
}
label={<span>User prompt</span>}
i18nStrings={{ errorIconAriaLabel: 'Error' }}
>
<PromptInput
data-testid="prompt-input"
ariaLabel="Chat input"
actionButtonIconName="send"
actionButtonAriaLabel="Submit prompt"
value={textareaValue}
onChange={(event: any) => setTextareaValue(event.detail.value)}
onAction={event => window.alert(`Submitted the following: ${event.detail.value}`)}
placeholder="Ask a question"
maxRows={4}
disabled={isDisabled}
readOnly={isReadOnly}
invalid={isInvalid || textareaValue.length > MAX_CHARS}
warning={hasWarning}
ref={ref}
disableSecondaryActionsPaddings={true}
secondaryActions={
hasSecondaryActions ? (
<Box padding={{ left: 'xxs', top: 'xs' }}>
<ButtonGroup
ariaLabel="Chat actions"
items={[
{
type: 'icon-button',
id: 'copy',
iconName: 'upload',
text: 'Upload files',
disabled: isDisabled || isReadOnly,
},
{
type: 'icon-button',
id: 'expand',
iconName: 'expand',
text: 'Go full page',
disabled: isDisabled || isReadOnly,
},
{
type: 'icon-button',
id: 'remove',
iconName: 'remove',
text: 'Remove',
disabled: isDisabled || isReadOnly,
},
]}
variant="icon"
/>
</Box>
) : undefined
}
secondaryContent={
hasSecondaryContent ? (
<TokenGroup
onDismiss={({ detail: { itemIndex } }) => {
setItems([...items.slice(0, itemIndex), ...items.slice(itemIndex + 1)]);
}}
items={items}
readOnly={isReadOnly}
/>
) : undefined
}
/>
</FormField>
<div />
</ColumnLayout>
</SpaceBetween>
</div>
}
splitPanel={
<SplitPanel header="Split panel header">
<PromptInput
data-testid="Prompt-input-in-split-panel"
value={valueInSplitPanel}
onChange={event => setValueInSplitPanel(event.detail.value)}
/>
</SplitPanel>
}
/>
);
}
18 changes: 13 additions & 5 deletions src/prompt-input/__integ__/prompt-input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import useBrowser from '@cloudscape-design/browser-test-tools/use-browser';

import createWrapper from '../../../lib/components/test-utils/selectors/index.js';

const promptInputWrapper = createWrapper().findPromptInput();
const getPromptInputWrapper = (testid = 'prompt-input') => createWrapper().findPromptInput(`[data-testid="${testid}"]`);

class PromptInputPage extends BasePageObject {
async getPromptInputHeight() {
const { height } = await this.getBoundingBox(promptInputWrapper.toSelector());
async getPromptInputHeight(testid = 'prompt-input') {
const { height } = await this.getBoundingBox(getPromptInputWrapper(testid).toSelector());
return height;
}
}
Expand All @@ -18,7 +18,7 @@ const setupTest = (testFn: (page: PromptInputPage) => Promise<void>) => {
return useBrowser(async browser => {
const page = new PromptInputPage(browser);
await browser.url(`#/light/prompt-input/simple/?isReadOnly=true`);
await page.waitForVisible(promptInputWrapper.toSelector());
await page.waitForVisible(getPromptInputWrapper().toSelector());
await testFn(page);
});
};
Expand All @@ -37,7 +37,15 @@ describe('Prompt input', () => {
setupTest(async page => {
await page.click('#focus-button');
await page.keys('Tab');
await expect(page.isFocused(promptInputWrapper.find('button').toSelector())).resolves.toBe(true);
await expect(page.isFocused(getPromptInputWrapper().find('button').toSelector())).resolves.toBe(true);
})
);

test(
'Should has one row height in Split Panel',
setupTest(async page => {
await page.click(createWrapper().findAppLayout().findSplitPanelOpenButton().toSelector());
await expect(page.getPromptInputHeight('Prompt-input-in-split-panel')).resolves.toEqual(32);
})
);
});
3 changes: 2 additions & 1 deletion src/prompt-input/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ const InternalPromptInput = React.forwardRef(
textareaRef.current.style.height = 'auto';
const maxRowsHeight = `calc(${maxRows <= 0 ? 3 : maxRows} * (${LINE_HEIGHT} + ${PADDING} / 2) + ${PADDING})`;
const scrollHeight = `calc(${textareaRef.current.scrollHeight}px)`;
textareaRef.current.style.height = `min(${scrollHeight}, ${maxRowsHeight})`;
const minRowsHeight = `calc(${LINE_HEIGHT} + ${tokens.spaceScaledXxs} * 2)`; // the min height of Textarea with 1 line
textareaRef.current.style.height = `min(max(${scrollHeight}, ${minRowsHeight}), ${maxRowsHeight})`;
}
}, [maxRows, LINE_HEIGHT, PADDING]);

Expand Down

0 comments on commit 4a561a9

Please sign in to comment.