Improvements for xs screens
This commit is contained in:
parent
68c247f3cc
commit
653a39d0a3
9 changed files with 146 additions and 25 deletions
|
@ -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 } }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>₿TClock</NavbarBrand>
|
<NavbarBrand class="d-none d-sm-block">₿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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue