Skip to content

Commit 7106f76

Browse files
committed
refactor: create independant form path input component
1 parent c9c034b commit 7106f76

File tree

4 files changed

+110
-1
lines changed

4 files changed

+110
-1
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { __ } from '@wordpress/i18n';
2+
import { useI18n } from '@wordpress/react-i18n';
3+
import FolderIcon from 'src/components/folder-icon';
4+
import { cx } from 'src/lib/cx';
5+
import { SiteFormError } from './site-form-error';
6+
7+
export interface FormPathInputComponentProps {
8+
value: string;
9+
onClick: () => void;
10+
error?: string;
11+
doesPathContainWordPress: boolean;
12+
id?: string;
13+
}
14+
15+
export function FormPathInputComponent( {
16+
value,
17+
onClick,
18+
error,
19+
doesPathContainWordPress,
20+
id,
21+
}: FormPathInputComponentProps ) {
22+
const { __ } = useI18n();
23+
return (
24+
<div className="flex flex-col gap-2">
25+
<button
26+
aria-invalid={ !! error }
27+
/**
28+
* The below `aria-describedby` presumes the error message always
29+
* relates to the local path input, which is true currently as it is the
30+
* only data validation in place. If we ever introduce additional data
31+
* validation we need to expand the robustness of this
32+
* `aria-describedby` attribute so that it only targets relevant error
33+
* messages.
34+
*/
35+
aria-describedby={ error ? 'site-path-error' : undefined }
36+
type="button"
37+
aria-label={ `${ value }, ${ __( 'Select different local path' ) }` }
38+
className={ cx(
39+
'flex flex-row items-stretch rounded-sm border border-[#949494] focus:border-a8c-blue-50 focus:shadow-[0_0_0_0.5px_black] focus:shadow-a8c-blue-50 outline-none transition-shadow transition-linear duration-100 [&_.local-path-icon]:focus:border-l-a8c-blue-50 [&:disabled]:cursor-not-allowed',
40+
error && 'border-red-500 [&_.local-path-icon]:border-l-red-500'
41+
) }
42+
data-testid="select-path-button"
43+
onClick={ onClick }
44+
id={ id }
45+
>
46+
<div
47+
aria-hidden="true"
48+
tabIndex={ -1 }
49+
className="w-full text-left pl-3 py-3 min-h-10"
50+
onChange={ () => {} }
51+
>
52+
{ value }
53+
</div>
54+
<div
55+
aria-hidden="true"
56+
className="local-path-icon flex items-center py-[9px] px-2.5 self-center"
57+
>
58+
<FolderIcon className="text-[#3C434A]" />
59+
</div>
60+
</button>
61+
<SiteFormError
62+
error={ error }
63+
tipMessage={
64+
doesPathContainWordPress
65+
? __( 'The existing WordPress site at this path will be added.' )
66+
: ''
67+
}
68+
/>
69+
<input type="hidden" data-testid="local-path-input" value={ value } />
70+
</div>
71+
);
72+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Icon } from '@wordpress/components';
2+
import { __ } from '@wordpress/i18n';
3+
import { cautionFilled, tip } from '@wordpress/icons';
4+
import { cx } from 'src/lib/cx';
5+
6+
export interface SiteFormErrorProps {
7+
error?: string;
8+
tipMessage?: string;
9+
className?: string;
10+
}
11+
12+
export const SiteFormError = ( { error, tipMessage = '', className = '' }: SiteFormErrorProps ) => {
13+
return (
14+
( error || tipMessage ) && (
15+
<div
16+
id={ error ? 'error-message' : 'tip-message' }
17+
role="alert"
18+
aria-atomic="true"
19+
className={ cx(
20+
'flex items-start gap-1 text-xs',
21+
error ? 'text-red-500' : 'text-a8c-gray-50',
22+
className
23+
) }
24+
>
25+
<Icon
26+
className={ cx( 'shrink-0 basis-4', error ? 'fill-red-500' : 'fill-a8c-gray-50' ) }
27+
icon={ error ? cautionFilled : tip }
28+
width={ 16 }
29+
height={ 16 }
30+
/>
31+
<p>{ error ? error : __( tipMessage ) }</p>
32+
</div>
33+
)
34+
);
35+
};

apps/studio/src/modules/add-site/components/create-site-form.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import { tip, cautionFilled, chevronRight, chevronDown, chevronLeft } from '@wor
1616
import { useI18n } from '@wordpress/react-i18n';
1717
import { FormEvent, useState, useEffect, useCallback, useMemo, useRef, RefObject } from 'react';
1818
import Button from 'src/components/button';
19-
import FolderIcon from 'src/components/folder-icon';
19+
import { FormPathInputComponent } from 'src/components/form-path-input';
2020
import { LearnMoreLink, LearnHowLink } from 'src/components/learn-more';
2121
import PasswordControl from 'src/components/password-control';
22+
import { SiteFormError } from 'src/components/site-form-error';
2223
import TextControlComponent from 'src/components/text-control';
2324
import { WPVersionSelector } from 'src/components/wp-version-selector';
2425
import { cx } from 'src/lib/cx';

apps/studio/src/modules/user-settings/components/preferences-tab.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { getIpcApi } from 'src/lib/get-ipc-api';
77
import { ColorSchemePicker } from 'src/modules/user-settings/components/color-scheme-picker';
88
import { McpSettings } from 'src/modules/mcp/components/mcp-settings';
99
import { getIpcApi } from 'src/lib/get-ipc-api';
10+
import { McpSettings } from 'src/modules/mcp/components/mcp-settings';
1011
import { EditorPicker } from 'src/modules/user-settings/components/editor-picker';
1112
import { LanguagePicker } from 'src/modules/user-settings/components/language-picker';
1213
import { StudioCliToggle } from 'src/modules/user-settings/components/studio-cli-toggle';

0 commit comments

Comments
 (0)