Improvements for xs screens
All checks were successful
/ check-changes (push) Successful in 5s
/ build (push) Successful in 3m57s

This commit is contained in:
Djuri Baars 2024-12-12 23:04:13 +01:00
parent 68c247f3cc
commit 653a39d0a3
9 changed files with 146 additions and 25 deletions

View file

@ -1,6 +1,7 @@
import { defineConfig, devices } from '@playwright/test'; import { defineConfig, devices } from '@playwright/test';
export default defineConfig({ export default defineConfig({
reporter: 'html',
use: { use: {
locale: 'en-GB', locale: 'en-GB',
timezoneId: 'Europe/Amsterdam' timezoneId: 'Europe/Amsterdam'
@ -31,6 +32,20 @@ export default defineConfig({
use: { use: {
viewport: { width: 1512, height: 982 } viewport: { width: 1512, height: 982 }
} }
},
{
name: 'MacBook Pro 14 inch',
use: {
viewport: { width: 1512, height: 982 }
}
},
{
name: 'MacBook Pro 14 inch Firefox HiDPI',
use: { ...devices['Desktop Firefox HiDPI'], viewport: { width: 1512, height: 982 } }
},
{
name: 'MacBook Pro 14 inch Safari',
use: { ...devices['Desktop Safari'], viewport: { width: 1512, height: 982 } }
} }
] ]
}); });

View file

@ -1,6 +1,4 @@
@import '../node_modules/bootstrap/scss/functions'; @import '../node_modules/bootstrap/scss/functions';
@import '../node_modules/bootstrap/scss/variables';
@import '../node_modules/bootstrap/scss/variables-dark';
//@import "@fontsource/antonio/latin-400.css"; //@import "@fontsource/antonio/latin-400.css";
@import '@fontsource/ubuntu/latin-400.css'; @import '@fontsource/ubuntu/latin-400.css';
@ -14,6 +12,8 @@ $font-family-base: 'Ubuntu';
$font-size-base: 0.9rem; $font-size-base: 0.9rem;
$input-font-size-sm: $font-size-base * 0.875; $input-font-size-sm: $font-size-base * 0.875;
@import '../node_modules/bootstrap/scss/variables';
@import '../node_modules/bootstrap/scss/variables-dark';
// $border-radius: .675rem; // $border-radius: .675rem;
@import '../node_modules/bootstrap/scss/mixins'; @import '../node_modules/bootstrap/scss/mixins';
@ -43,6 +43,40 @@ $input-font-size-sm: $font-size-base * 0.875;
@import '../node_modules/bootstrap/scss/helpers'; @import '../node_modules/bootstrap/scss/helpers';
@import '../node_modules/bootstrap/scss/utilities/api'; @import '../node_modules/bootstrap/scss/utilities/api';
/* Default state (xs) - sticky */
.sticky-xs-top {
position: sticky;
top: 0;
z-index: 1020;
}
@media (max-width: 576px) {
main {
margin-top: 25px;
}
}
/* Remove sticky behavior for larger screens */
@media (min-width: 576px) {
.sticky-xs-top {
position: relative;
}
}
@include color-mode(dark) {
.navbar {
--bs-navbar-color: $light;
background-color: $dark;
}
}
@include color-mode(light) {
.navbar {
--bs-navbar-color: $dark;
background-color: $light;
}
}
nav { nav {
margin-bottom: 15px; margin-bottom: 15px;
} }

View file

@ -12,6 +12,7 @@
NavbarBrand, NavbarBrand,
NavbarToggler NavbarToggler
} from '@sveltestrap/sveltestrap'; } from '@sveltestrap/sveltestrap';
import { _ } from 'svelte-i18n';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { locale, locales, isLoading } from 'svelte-i18n'; import { locale, locales, isLoading } from 'svelte-i18n';
@ -38,7 +39,7 @@
return flagMap[lowercaseCode]; return flagMap[lowercaseCode];
} else { } else {
// Return null for unsupported language codes // Return null for unsupported language codes
return null; return flagMap['en'];
} }
}; };
@ -61,8 +62,23 @@
}; };
</script> </script>
<Navbar expand="md"> <Navbar expand="md" sticky="xs-top" theme="auto">
<NavbarBrand>&#8383;TClock</NavbarBrand> <NavbarBrand class="d-none d-sm-block">&#8383;TClock</NavbarBrand>
<Nav class="d-md-none" pills>
<NavItem>
<NavLink href="#control" active>{$_('section.control.title', { default: 'Control' })}</NavLink
>
</NavItem>
<NavItem>
<NavLink href="#status">{$_('section.status.title', { default: 'Status' })}</NavLink>
</NavItem>
<NavItem>
<NavLink class="nav-link" href="#settings"
>{$_('section.settings.title', { default: 'Settings' })}</NavLink
>
</NavItem>
</Nav>
<NavbarToggler on:click={toggle} /> <NavbarToggler on:click={toggle} />
<Collapse {isOpen} navbar expand="sm"> <Collapse {isOpen} navbar expand="sm">
@ -94,4 +110,6 @@
</Navbar> </Navbar>
<!-- +layout.svelte --> <!-- +layout.svelte -->
<slot /> <main>
<slot />
</main>

View file

@ -3,6 +3,7 @@
import { screenSize, updateScreenSize } from '$lib/screen'; import { screenSize, updateScreenSize } from '$lib/screen';
import { Container, Row, Toast, ToastBody } from '@sveltestrap/sveltestrap'; import { Container, Row, Toast, ToastBody } from '@sveltestrap/sveltestrap';
import { replaceState } from '$app/navigation';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
@ -16,12 +17,6 @@
bgColor: '0' bgColor: '0'
}); });
// let uiSettings = writable({
// inputSize: 'sm',
// selectClass: '',
// btnSize: 'lg'
// });
let status = writable({ let status = writable({
data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'], data: ['L', 'O', 'A', 'D', 'I', 'N', 'G'],
espFreeHeap: 0, espFreeHeap: 0,
@ -60,7 +55,43 @@
}); });
}; };
let sections: (HTMLElement | null)[];
let observer: IntersectionObserver;
const SM_BREAKPOINT = 576;
const setupObserver = () => {
if (window.innerWidth < SM_BREAKPOINT) {
observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const id = entry.target.id;
replaceState(`#${id}`);
// Update nav pills
document.querySelectorAll('.nav-link').forEach((link) => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${id}`) {
link.classList.add('active');
}
});
}
});
},
{
threshold: 0.25 // Trigger when section is 50% visible
}
);
sections = ['control', 'status', 'settings'].map((id) => document.getElementById(id));
sections.forEach((section) => observer.observe(section!));
}
};
onMount(() => { onMount(() => {
setupObserver();
fetchSettingsData(); fetchSettingsData();
fetchStatusData(); fetchStatusData();
@ -72,6 +103,11 @@
}); });
function handleResize() { function handleResize() {
if (observer) {
observer.disconnect();
}
setupObserver();
updateScreenSize(); updateScreenSize();
} }
@ -125,7 +161,9 @@
<Container fluid> <Container fluid>
<Row> <Row>
<Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control> <Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control>
<Status bind:settings bind:status lg="6" xxl="4"></Status> <Status bind:settings bind:status lg="6" xxl="4"></Status>
<Settings bind:settings on:showToast={showToast} on:formReset={fetchSettingsData} lg="3" xxl="4" <Settings bind:settings on:showToast={showToast} on:formReset={fetchSettingsData} lg="3" xxl="4"
></Settings> ></Settings>
</Row> </Row>

View file

@ -105,8 +105,8 @@
export let xxl = xl; export let xxl = xl;
</script> </script>
<Col {xs} {sm} {md} {lg} {xl} {xxl}> <Col {xs} {sm} {md} {lg} {xl} {xxl} class="mb-4 mb-xl-0">
<Card> <Card id="control">
<CardHeader> <CardHeader>
<CardTitle>{$_('section.control.title', { default: 'Control' })}</CardTitle> <CardTitle>{$_('section.control.title', { default: 'Control' })}</CardTitle>
</CardHeader> </CardHeader>

View file

@ -221,8 +221,8 @@
systemIsOpen: boolean; systemIsOpen: boolean;
</script> </script>
<Col {xs} {sm} {md} {lg} {xl} {xxl}> <Col {xs} {sm} {md} {lg} {xl} {xxl} class="mb-4 mb-xl-0">
<Card> <Card id="settings">
<CardHeader> <CardHeader>
<div class="float-end"> <div class="float-end">
<small <small

View file

@ -104,8 +104,8 @@
export let xxl = xl; export let xxl = xl;
</script> </script>
<Col {xs} {sm} {md} {lg} {xl} {xxl}> <Col {xs} {sm} {md} {lg} {xl} {xxl} class="mb-4 mb-xl-0">
<Card> <Card id="status">
<CardHeader> <CardHeader>
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle> <CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
</CardHeader> </CardHeader>

View file

@ -4,19 +4,23 @@ import { initMock, settingsJson } from '../shared';
test.beforeEach(initMock); test.beforeEach(initMock);
test('capture screenshots across devices', async ({ page }) => { test('capture screenshots across devices', async ({ page }, testInfo) => {
await page.goto('/'); await page.goto('/');
await expect(page.getByRole('heading', { name: 'Control' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Control' })).toBeVisible();
await expect(page.getByRole('heading', { name: 'Status' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Status' })).toBeVisible();
await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible();
await page.screenshot({ const screenshot = await page.screenshot({
path: `./test-results/screenshots/default-${test.info().project.name}.png`, path: `./test-results/screenshots/default-${test.info().project.name.toLowerCase().replace(' ', '_')}.png`
fullPage: true });
await testInfo.attach(`default`, {
body: screenshot,
contentType: 'image/png'
}); });
}); });
test('capture screenshots across devices with bitaxe screens', async ({ page }) => { test('capture screenshots across devices with bitaxe screens', async ({ page }, testInfo) => {
settingsJson.screens = [ settingsJson.screens = [
{ {
id: 0, id: 0,
@ -71,7 +75,11 @@ test('capture screenshots across devices with bitaxe screens', async ({ page })
await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible();
await page.screenshot({ await page.screenshot({
path: `./test-results/screenshots/bitaxe-${test.info().project.name}.png`, path: `./test-results/screenshots/bitaxe-${test.info().project.name.toLowerCase().replace(' ', '_')}.png`
fullPage: true });
await testInfo.attach(`bitaxe`, {
path: `./test-results/screenshots/bitaxe-${test.info().project.name.toLowerCase().replace(' ', '_')}.png`,
contentType: 'image/png'
}); });
}); });

View file

@ -76,6 +76,14 @@ export default defineConfig({
} }
} }
}, },
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
silenceDeprecations: ['import']
}
}
},
test: { test: {
include: ['src/**/*.{test,spec}.{js,ts}'], include: ['src/**/*.{test,spec}.{js,ts}'],
globals: true, globals: true,