Replace all remaining bootstrap utility classes with tailwind classes

This commit is contained in:
Alejandro Celaya 2025-04-05 12:14:27 +02:00
parent d188d67c5a
commit 5e0db07ef3
9 changed files with 39 additions and 50 deletions

View File

@ -45,26 +45,24 @@ export const Home = ({ servers }: HomeProps) => {
>
Welcome!
</h1>
<ServersListGroup servers={serversList}>
{!hasServers && (
<div className="tw:p-6 tw:text-center tw:flex tw:flex-col tw:gap-12">
<p>This application will help you manage your Shlink servers.</p>
<p>
<Button to="/server/create" size="lg" inline>
<FontAwesomeIcon icon={faPlus} /> Add a server
</Button>
</p>
<p>
<ExternalLink href="https://shlink.io/documentation">
<small>
<span className="tw:mr-2">Learn more about Shlink</span>
<FontAwesomeIcon icon={faExternalLinkAlt} />
</small>
</ExternalLink>
</p>
</div>
)}
</ServersListGroup>
{hasServers ? <ServersListGroup servers={serversList} /> : (
<div className="tw:p-6 tw:text-center tw:flex tw:flex-col tw:gap-12 tw:text-xl">
<p>This application will help you manage your Shlink servers.</p>
<p>
<Button to="/server/create" size="lg" inline>
<FontAwesomeIcon icon={faPlus} /> Add a server
</Button>
</p>
<p>
<ExternalLink href="https://shlink.io/documentation">
<small>
<span className="tw:mr-2">Learn more about Shlink</span>
<FontAwesomeIcon icon={faExternalLinkAlt} />
</small>
</ExternalLink>
</p>
</div>
)}
</div>
</div>
</Card>

View File

@ -63,13 +63,17 @@ const ManageServers: FCWithDeps<ManageServersProps, ManageServersDeps> = ({ serv
<SimpleCard className="card">
<Table header={(
<Table.Row>
{hasAutoConnect && <Table.Cell style={{ width: '50px' }}><span className="sr-only">Auto-connect</span></Table.Cell>}
{hasAutoConnect && (
<Table.Cell className="tw:w-[35px]"><span className="tw:sr-only">Auto-connect</span></Table.Cell>
)}
<Table.Cell>Name</Table.Cell>
<Table.Cell>Base URL</Table.Cell>
<Table.Cell><span className="sr-only">Options</span></Table.Cell>
</Table.Row>
)}>
{!filteredServers.length && <Table.Row className="text-center"><Table.Cell colSpan={4}>No servers found.</Table.Cell></Table.Row>}
{!filteredServers.length && (
<Table.Row className="tw:text-center"><Table.Cell colSpan={4}>No servers found.</Table.Cell></Table.Row>
)}
{filteredServers.map((server) => (
<ManageServersRow key={server.id} server={server} hasAutoConnect={hasAutoConnect} />
))}

View File

@ -27,7 +27,7 @@ const ManageServersRow: FCWithDeps<ManageServersRowProps, ManageServersRowDeps>
<Table.Cell columnName="Auto-connect">
{server.autoConnect && (
<>
<FontAwesomeIcon icon={checkIcon} className="text-primary" id="autoConnectIcon" />
<FontAwesomeIcon icon={checkIcon} className="tw:text-brand" id="autoConnectIcon" />
<UncontrolledTooltip target="autoConnectIcon" placement="right">
Auto-connect to this server
</UncontrolledTooltip>

View File

@ -17,7 +17,7 @@ export const ServersDropdown = ({ servers, selectedServer }: ServersDropdownProp
if (serversList.length === 0) {
return (
<DropdownItem tag={Link} to="/server/create">
<FontAwesomeIcon icon={plusIcon} /> <span className="ms-1">Add a server</span>
<FontAwesomeIcon icon={plusIcon} /> <span className="tw:ml-1">Add a server</span>
</DropdownItem>
);
}
@ -31,7 +31,7 @@ export const ServersDropdown = ({ servers, selectedServer }: ServersDropdownProp
))}
<DropdownItem divider tag="hr" />
<DropdownItem tag={Link} to="/manage-servers">
<FontAwesomeIcon icon={serverIcon} /> <span className="ms-1">Manage servers</span>
<FontAwesomeIcon icon={serverIcon} /> <span className="tw:ml-1">Manage servers</span>
</DropdownItem>
</>
);
@ -40,9 +40,9 @@ export const ServersDropdown = ({ servers, selectedServer }: ServersDropdownProp
return (
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret>
<FontAwesomeIcon icon={serverIcon} /> <span className="ms-1">Servers</span>
<FontAwesomeIcon icon={serverIcon} /> <span className="tw:ml-1">Servers</span>
</DropdownToggle>
<DropdownMenu end style={{ right: 0 }}>{renderServers()}</DropdownMenu>
<DropdownMenu end classNam="tw:right-0">{renderServers()}</DropdownMenu>
</UncontrolledDropdown>
);
};

View File

@ -1,14 +1,14 @@
import { faChevronRight as chevronIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { clsx } from 'clsx';
import type { FC, PropsWithChildren } from 'react';
import type { FC } from 'react';
import { Link } from 'react-router';
import type { ServerWithId } from './data';
type ServersListGroupProps = PropsWithChildren<{
type ServersListGroupProps = {
servers: ServerWithId[];
borderless?: boolean;
}>;
};
const ServerListItem = ({ id, name }: { id: string; name: string }) => (
<Link
@ -25,9 +25,8 @@ const ServerListItem = ({ id, name }: { id: string; name: string }) => (
</Link>
);
export const ServersListGroup: FC<ServersListGroupProps> = ({ servers, children, borderless }) => (
export const ServersListGroup: FC<ServersListGroupProps> = ({ servers, borderless }) => (
<>
{children && <div data-testid="title" className="fs-5 fw-normal lh-sm">{children}</div>}
{servers.length > 0 && (
<div
data-testid="list"

View File

@ -100,7 +100,7 @@ const ImportServersBtn: FCWithDeps<ImportServersBtnConnectProps, ImportServersBt
<input
type="file"
accept=".csv"
className="d-none"
className="tw:hidden"
aria-hidden
ref={ref as any /* TODO Remove After updating to React 19 */}
onChange={onFile}

View File

@ -17,7 +17,7 @@ export const ServerForm: FC<ServerFormProps> = ({ onSubmit, initialValues, child
const handleSubmit = handleEventPreventingDefault(() => onSubmit({ name, url, apiKey }));
return (
<form className="server-form" name="serverForm" onSubmit={handleSubmit}>
<form name="serverForm" onSubmit={handleSubmit}>
<SimpleCard className="tw:mb-4" bodyClassName="tw:flex tw:flex-col tw:gap-y-3" title={title}>
<LabelledInput label="Name" value={name} onChange={(e) => setName(e.target.value)} required />
<LabelledInput label="URL" type="url" value={url} onChange={(e) => setUrl(e.target.value)} required />

View File

@ -10,30 +10,18 @@ describe('<ServersListGroup />', () => {
fromPartial({ name: 'foo', id: '123' }),
fromPartial({ name: 'bar', id: '456' }),
];
const setUp = (params: { servers?: ServerWithId[]; withChildren?: boolean; borderless?: boolean } = {}) => {
const { servers = [], withChildren = true, borderless } = params;
const setUp = (params: { servers?: ServerWithId[]; borderless?: boolean } = {}) => {
const { servers = [], borderless } = params;
return render(
<MemoryRouter>
<ServersListGroup servers={servers} borderless={borderless}>
{withChildren ? 'The list of servers' : undefined}
</ServersListGroup>
<ServersListGroup servers={servers} borderless={borderless} />
</MemoryRouter>,
);
};
it('passes a11y checks', () => checkAccessibility(setUp()));
it('renders title', () => {
setUp({});
expect(screen.getByTestId('title')).toHaveTextContent('The list of servers');
});
it('does not render title when children is not provided', () => {
setUp({ withChildren: false });
expect(screen.queryByTestId('title')).not.toBeInTheDocument();
});
it.each([
[servers],
[[]],

View File

@ -24,7 +24,7 @@ exports[`<ManageServersRow /> > renders auto-connect icon only if server is auto
>
<svg
aria-hidden="true"
class="svg-inline--fa fa-check text-primary"
class="svg-inline--fa fa-check tw:text-brand"
data-icon="check"
data-prefix="fas"
focusable="false"