u
This commit is contained in:
300
src/dev/screenshots/ScreenshotHarness.jsx
Normal file
300
src/dev/screenshots/ScreenshotHarness.jsx
Normal file
@@ -0,0 +1,300 @@
|
||||
import { Component } from 'react';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import Box from '@mui/material/Box';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import FolderIcon from '@mui/icons-material/FolderOutlined';
|
||||
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
|
||||
import ToggleOnIcon from '@mui/icons-material/ToggleOn';
|
||||
import { EditablePanelTile, PanelTile, PanelTileGrid } from '../../panels';
|
||||
import RecordList from '../../lists/RecordList';
|
||||
import { Tile, tileBaseSx } from '../../tiles';
|
||||
import ElevatedPill, {
|
||||
ActionElevatedPill,
|
||||
ToggleElevatedPill,
|
||||
} from '../../ElevatedPill';
|
||||
import SingleLineTextEdit from '../../SingleLineTextEdit';
|
||||
|
||||
const MOCK_ROWS = [
|
||||
{
|
||||
id: 1,
|
||||
number: 'L-1001',
|
||||
date: '2026-08-01',
|
||||
party: 'Müller GmbH',
|
||||
status: 'fertig',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
number: 'L-1002',
|
||||
date: '2026-08-03',
|
||||
party: 'Schmidt & Co',
|
||||
status: 'offen',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
number: 'L-1003',
|
||||
date: '2026-08-05',
|
||||
party: 'Weber OHG',
|
||||
status: 'offen',
|
||||
},
|
||||
];
|
||||
|
||||
const COLUMNS = [
|
||||
{ key: 'number', label: 'Nummer' },
|
||||
{ key: 'date', label: 'Lieferdatum' },
|
||||
{ key: 'party', label: 'Lieferant' },
|
||||
{
|
||||
key: 'status',
|
||||
label: 'Status',
|
||||
render: (row) => (
|
||||
<Chip
|
||||
size="small"
|
||||
label={row.status}
|
||||
color={row.status === 'fertig' ? 'success' : 'default'}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
class MockFolderTile extends Tile {
|
||||
renderIcon() {
|
||||
return <FolderIcon color="action" sx={{ fontSize: 40 }} />;
|
||||
}
|
||||
}
|
||||
|
||||
class MockAddTile extends Tile {
|
||||
renderIcon() {
|
||||
return <AddIcon color="primary" sx={{ fontSize: 40 }} />;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed, deterministic gallery of every GUI variation — one `data-shot`
|
||||
* section per reference screenshot. Served under /screenshots.html and
|
||||
* captured by the Playwright screenshot tests (tests/screenshot.spec.js).
|
||||
*
|
||||
* Deliberately static: no timers, no routing, no user state. Only stable
|
||||
* props, so references stay comparable across runs. Interactive states
|
||||
* (EditablePanelTile editing/dirty) are driven from the test, not here.
|
||||
*/
|
||||
function Section({ id, label, children }) {
|
||||
return (
|
||||
<Box
|
||||
component="section"
|
||||
data-shot={id}
|
||||
sx={{ background: '#f4f4f5', p: 2, borderRadius: 1, mb: 3 }}
|
||||
>
|
||||
<Typography
|
||||
variant="overline"
|
||||
color="text.secondary"
|
||||
sx={{ display: 'block', mb: 1.5 }}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
class ScreenshotHarness extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Box sx={{ width: 1000, mx: 'auto', py: 3, px: 1 }}>
|
||||
<Section id="pills" label="ElevatedPill / Toggle / Action">
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<ElevatedPill label="Statisch" />
|
||||
<ElevatedPill label="Aktiv" active />
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<ToggleElevatedPill label="Aus" checked={false} onToggle={() => {}} />
|
||||
<ToggleElevatedPill label="An" checked onToggle={() => {}} />
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<ActionElevatedPill
|
||||
label="Inaktiv"
|
||||
icon={<ToggleOnIcon sx={{ fontSize: 18 }} />}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<ActionElevatedPill
|
||||
label="Aktiv"
|
||||
active
|
||||
icon={<ToggleOnIcon sx={{ fontSize: 18 }} />}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Section>
|
||||
|
||||
<Section id="textfields" label="SingleLineTextEdit">
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
maxWidth: 480,
|
||||
}}
|
||||
>
|
||||
<SingleLineTextEdit label="Name" value="ACME GmbH" onChange={() => {}} />
|
||||
<SingleLineTextEdit
|
||||
label="Name"
|
||||
value=""
|
||||
onChange={() => {}}
|
||||
helperText="Mit Hilfetext"
|
||||
/>
|
||||
<SingleLineTextEdit
|
||||
label="Nur lesen"
|
||||
value="Schreibgeschützt"
|
||||
readOnly
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<SingleLineTextEdit
|
||||
label="Deaktiviert"
|
||||
value="Ausgegraut"
|
||||
disabled
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<SingleLineTextEdit
|
||||
label="Klein"
|
||||
value="Small"
|
||||
size="small"
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</Box>
|
||||
</Section>
|
||||
|
||||
<Section id="tiles" label="Tile / tileBaseSx">
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(4, 140px)',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<MockFolderTile label="Ordner" secondary="3 Artikel" />
|
||||
<MockAddTile label="Neu" secondary="anlegen" />
|
||||
<MockFolderTile label="Deaktiviert" secondary="nicht klickbar" disabled />
|
||||
<Box sx={{ ...tileBaseSx, bgcolor: 'primary.main', color: 'white' }}>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
tileBaseSx direkt
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Section>
|
||||
|
||||
<Section id="panels-basic" label="PanelTile">
|
||||
<PanelTileGrid>
|
||||
<PanelTile wide={2} title="Firma">
|
||||
<Typography variant="body1">ACME GmbH</Typography>
|
||||
</PanelTile>
|
||||
<PanelTile wide={1} title="Farbe" color="#fef3c7">
|
||||
<Typography variant="body2">Mit Hintergrundfarbe</Typography>
|
||||
</PanelTile>
|
||||
<PanelTile
|
||||
wide={1}
|
||||
title="Mit Aktion"
|
||||
action={{ label: 'Speichern', icon: SaveOutlinedIcon, onClick: () => {} }}
|
||||
>
|
||||
<Typography variant="body2">Idle Aktion</Typography>
|
||||
</PanelTile>
|
||||
</PanelTileGrid>
|
||||
</Section>
|
||||
|
||||
<Section id="panels-busy" label="PanelTile – Aktion busy">
|
||||
<PanelTileGrid>
|
||||
<PanelTile
|
||||
wide={2}
|
||||
title="Speichern läuft"
|
||||
action={{
|
||||
label: 'Speichern',
|
||||
icon: SaveOutlinedIcon,
|
||||
busy: true,
|
||||
onClick: () => {},
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1">Wird gespeichert …</Typography>
|
||||
</PanelTile>
|
||||
</PanelTileGrid>
|
||||
</Section>
|
||||
|
||||
<Section id="editable" label="EditablePanelTile">
|
||||
<PanelTileGrid>
|
||||
<EditablePanelTile
|
||||
wide={2}
|
||||
title="Firma"
|
||||
value="ACME GmbH"
|
||||
onSave={async () => {}}
|
||||
onError={() => {}}
|
||||
display={<Typography variant="body1">ACME GmbH</Typography>}
|
||||
edit={({ draft, onChange, inputRef }) => (
|
||||
<SingleLineTextEdit
|
||||
label="Name"
|
||||
value={draft}
|
||||
onChange={onChange}
|
||||
inputRef={inputRef}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</PanelTileGrid>
|
||||
</Section>
|
||||
|
||||
<Section id="lists-ready" label="RecordList – ready">
|
||||
<RecordList
|
||||
status="ready"
|
||||
rows={MOCK_ROWS}
|
||||
columns={COLUMNS}
|
||||
createLabel="Neue Lieferung"
|
||||
onCreate={() => {}}
|
||||
onEdit={() => {}}
|
||||
onDelete={() => {}}
|
||||
emptyMessage="Noch keine Lieferungen."
|
||||
color="#fed7aa"
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section id="lists-empty" label="RecordList – leer">
|
||||
<RecordList
|
||||
status="ready"
|
||||
rows={[]}
|
||||
columns={COLUMNS}
|
||||
createLabel="Neue Lieferung"
|
||||
onCreate={() => {}}
|
||||
onEdit={() => {}}
|
||||
onDelete={() => {}}
|
||||
emptyMessage="Noch keine Lieferungen."
|
||||
color="#fed7aa"
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section id="lists-loading" label="RecordList – laden">
|
||||
<RecordList status="loading" rows={[]} columns={COLUMNS} />
|
||||
</Section>
|
||||
|
||||
<Section id="lists-failed" label="RecordList – Fehler">
|
||||
<RecordList
|
||||
status="failed"
|
||||
rows={[]}
|
||||
columns={COLUMNS}
|
||||
error={
|
||||
<Alert severity="error">
|
||||
Backend-Fehler: Verbindung fehlgeschlagen.
|
||||
</Alert>
|
||||
}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section id="lists-no-actions" label="RecordList – ohne Aktionen">
|
||||
<RecordList
|
||||
status="ready"
|
||||
rows={MOCK_ROWS}
|
||||
columns={COLUMNS}
|
||||
color="#e2e8f0"
|
||||
/>
|
||||
</Section>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { ScreenshotHarness };
|
||||
17
src/dev/screenshotsMain.jsx
Normal file
17
src/dev/screenshotsMain.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { ScreenshotHarness } from './screenshots/ScreenshotHarness';
|
||||
|
||||
// Standalone root for the screenshot harness (/screenshots.html). Mirrors the
|
||||
// dev gallery's theme so reference screenshots match real-app rendering.
|
||||
const theme = createTheme({
|
||||
shape: { borderRadius: 4 },
|
||||
});
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<ScreenshotHarness />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
Reference in New Issue
Block a user