Skip to content

Commit f3f0cee

Browse files
committed
Start adding authors to test env
1 parent fb77421 commit f3f0cee

File tree

8 files changed

+235
-15
lines changed

8 files changed

+235
-15
lines changed

CHANGELOG.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# v0.18.0
22

3-
* **BC Break**: Rename `Date` filter to `DateRange`. A text filter can be used for precise dates.
4-
* Fix an issue with top right header where icons and tooltips were not using the proper API and props.
5-
* Fix the fuzzy behavior of the `DateRange` filter, making it more explicit.
6-
* Fixed/changed the `getSubmittedFormData()` function's behavior: it now supports cases when you have the same `name` several times in Form data (via `<input name="...">` for instance), so that the processed data is properly converted to an array.<br>Check the `src/lib/Crud/Form.test.ts` file for more details about how it works.
7-
* Create script to create new theme based on Carbon (for future theming, again).
3+
- **BC Break**: Rename `Date` filter to `DateRange`. A text filter can be used for precise dates.
4+
- Fix an issue with top right header where icons and tooltips were not using the proper API and props.
5+
- Fix the fuzzy behavior of the `DateRange` filter, making it more explicit.
6+
- Fixed/changed the `getSubmittedFormData()` function's behavior: it now supports cases when you have the same `name` several times in Form data (via `<input name="...">` for instance), so that the processed data is properly converted to an array.<br>Check the `src/lib/Crud/Form.test.ts` file for more details about how it works.
7+
- Create script to create new theme based on Carbon (for future theming, again).
88

99
# v0.17.0
1010

src/lib/Crud/Form.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ describe('Submitted form data', () => {
5353
const submitted = getSubmittedFormData(
5454
mockSubmitEvent([
5555
['title', 'First title'],
56-
['title', 'Second title'],
56+
['title', 'Second title']
5757
])
5858
);
5959

6060
expect(submitted).toStrictEqual({
61-
title: ['First title', 'Second title'],
61+
title: ['First title', 'Second title']
6262
});
6363
},
6464
testOptions

src/lib/themes/svelte/carbon/lib/ThemeChangeMenu.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ import { type MenuLink, Submenu } from '$lib/Menu';
33

44
type Optional<T> = T | null | undefined;
55

6-
type CarbonTheme = "white"|"g10"|"g80"|"g90"|"g100";
6+
type CarbonTheme = 'white' | 'g10' | 'g80' | 'g90' | 'g100';
77

8-
export default class ThemeChangeMenu extends Submenu
9-
{
10-
static readonly availableThemes: Array<CarbonTheme> = ["white", "g10", "g80", "g90", "g100"];
8+
export default class ThemeChangeMenu extends Submenu {
9+
static readonly availableThemes: Array<CarbonTheme> = ['white', 'g10', 'g80', 'g90', 'g100'];
1110

1211
constructor(icon?: Optional<ActionIcon>, options?: ActionOptions) {
1312
const links: Array<MenuLink> = ThemeChangeMenu.availableThemes.map((theme) => {
14-
return new CallbackAction('carbon.theme.'+theme, undefined, () => this.changeTheme(theme));
13+
return new CallbackAction('carbon.theme.' + theme, undefined, () => this.changeTheme(theme));
1514
});
1615

1716
super('carbon.theme.change_action', icon, links, options);

src/lib/translations/en.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const dictionary: Dictionary = {
4040
'carbon.theme.g10': 'Light gray (g10)',
4141
'carbon.theme.g80': 'Gray (g80)',
4242
'carbon.theme.g90': 'Dark gray (g90)',
43-
'carbon.theme.g100': 'Dark (g100)',
43+
'carbon.theme.g100': 'Dark (g100)'
4444
};
4545

4646
export default dictionary;

src/lib/translations/fr.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const dictionary: Dictionary = {
4141
'carbon.theme.g10': 'Gris clair (g10)',
4242
'carbon.theme.g80': 'Gris (g80)',
4343
'carbon.theme.g90': 'Gris foncé (g90)',
44-
'carbon.theme.g100': 'Sombre (g100)',
44+
'carbon.theme.g100': 'Sombre (g100)'
4545
};
4646

4747
export default dictionary;

src/testApp/AuthorCrud.ts

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// src/lib/AuthorCrud.ts
2+
import {
3+
CallbackAction,
4+
CallbackStateProcessor,
5+
CallbackStateProvider,
6+
CrudDefinition,
7+
DateField,
8+
DateRangeFilter,
9+
Delete,
10+
Edit,
11+
List,
12+
New,
13+
PaginatedResults,
14+
TextareaField,
15+
TextField,
16+
TextFilter,
17+
type RequestParameters,
18+
UrlAction,
19+
View
20+
} from '$lib';
21+
22+
import { faker } from '@faker-js/faker';
23+
24+
import Pen from 'carbon-icons-svelte/lib/Pen.svelte';
25+
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
26+
import ViewIcon from 'carbon-icons-svelte/lib/View.svelte';
27+
import { type Author, getStorage } from './internal/authorsInternal';
28+
29+
const fields = [
30+
new TextField('first_name', 'First name'),
31+
new TextField('last_name', 'Last name'),
32+
new TextareaField('Bio', 'Biography')
33+
];
34+
35+
const IdField = new TextField('id', 'ID');
36+
37+
const itemsPerPage = 10;
38+
39+
function randomWait(maxMilliseconds: number) {
40+
return new Promise((resolve: (...args: unknown[]) => unknown) =>
41+
setTimeout(resolve, Math.random() * maxMilliseconds)
42+
);
43+
}
44+
45+
export const authorCrud = new CrudDefinition<Author>({
46+
name: 'authors',
47+
label: { singular: 'Author', plural: 'Authors' },
48+
minStateLoadingTimeMs: 400,
49+
50+
operations: [
51+
new List(
52+
[IdField, ...fields],
53+
[
54+
new UrlAction('View', '/admin/authors/view', ViewIcon),
55+
new UrlAction('Edit', '/admin/authors/edit', Pen),
56+
new UrlAction('Delete', '/admin/authors/delete', TrashCan)
57+
],
58+
{
59+
globalActions: [
60+
new CallbackAction(
61+
'Reset memory data',
62+
TrashCan,
63+
() => {
64+
window.localStorage.removeItem('authors');
65+
window.location.reload();
66+
},
67+
{ buttonKind: 'ghost' }
68+
),
69+
new UrlAction('New', '/admin/authors/new', Pen)
70+
],
71+
pagination: {
72+
enabled: true,
73+
itemsPerPage: itemsPerPage
74+
},
75+
filters: [
76+
new TextFilter('first_name', 'First name contains'),
77+
new TextFilter('last_name', 'Last name contains'),
78+
new TextFilter('bio', 'Biography contains')
79+
]
80+
}
81+
),
82+
new View([IdField, ...fields]),
83+
new New(fields),
84+
new Edit(fields),
85+
new Delete(fields, new UrlAction('List', '/admin/authors/list'))
86+
],
87+
88+
stateProcessor: new CallbackStateProcessor(function (
89+
data,
90+
operation,
91+
requestParameters: RequestParameters = {}
92+
) {
93+
if (operation.name === 'delete') {
94+
const id = (requestParameters.id || '').toString();
95+
getStorage().remove(id);
96+
97+
return Promise.resolve();
98+
}
99+
100+
if (operation.name === 'edit' || operation.name === 'new') {
101+
const id =
102+
operation.name === 'edit' ? (requestParameters.id || '').toString() : faker.string.uuid();
103+
const entity = data as Author;
104+
entity.id = id;
105+
106+
if (operation.name === 'new') {
107+
getStorage().add(entity);
108+
} else {
109+
getStorage().update(entity);
110+
}
111+
112+
return Promise.resolve();
113+
}
114+
115+
console.error(
116+
'StateProcessor error: Unsupported Authors Crud action "' + operation.name + '".'
117+
);
118+
119+
return Promise.resolve();
120+
}),
121+
122+
stateProvider: new CallbackStateProvider<Author>(async function (
123+
operation,
124+
requestParameters: RequestParameters = {}
125+
) {
126+
console.info('Authors provider called', { operation: operation.name, requestParameters });
127+
128+
if (operation.name === 'list') {
129+
const page = parseInt((requestParameters.page || '1').toString());
130+
if (isNaN(page)) {
131+
throw new Error(`Invalid "page" value: expected a number, got "${page}".`);
132+
}
133+
134+
let entities = getStorage().all();
135+
const filters = requestParameters.filters;
136+
if (filters) {
137+
entities = entities.filter((entity) => {
138+
if (
139+
filters.first_name &&
140+
!entity.first_name.match(new RegExp(filters.first_name.toString(), 'gi'))
141+
) {
142+
return false;
143+
}
144+
if (
145+
filters.last_name &&
146+
!entity.last_name.match(new RegExp(filters.last_name.toString(), 'gi'))
147+
) {
148+
return false;
149+
}
150+
if (filters.bio && !entity.bio.match(new RegExp(filters.bio.toString(), 'gi'))) {
151+
return false;
152+
}
153+
154+
return true;
155+
});
156+
}
157+
158+
const listEntities = entities.slice(itemsPerPage * (page - 1), itemsPerPage * page);
159+
160+
await randomWait(500);
161+
162+
return new PaginatedResults(
163+
page,
164+
Math.ceil(entities.length / itemsPerPage),
165+
entities.length,
166+
listEntities
167+
);
168+
}
169+
170+
if (operation.name === 'edit' || operation.name === 'view') {
171+
return Promise.resolve(getStorage().get((requestParameters?.id || '').toString()));
172+
}
173+
174+
if (operation.name === 'entity_view') {
175+
return Promise.resolve(getStorage().get((requestParameters?.field_value || '').toString()));
176+
}
177+
178+
if (operation.name === 'entity_list') {
179+
return Promise.resolve(getStorage().all());
180+
}
181+
182+
console.error('StateProvider error: Unsupported Authors Crud action "' + operation.name + '".');
183+
184+
return Promise.resolve(null);
185+
})
186+
});

src/testApp/Dashboard.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import Document from 'carbon-icons-svelte/lib/Document.svelte';
33
import Home from 'carbon-icons-svelte/lib/Home.svelte';
44
import Menu from 'carbon-icons-svelte/lib/Menu.svelte';
55
import Switcher from 'carbon-icons-svelte/lib/Switcher.svelte';
6+
import User from 'carbon-icons-svelte/lib/User.svelte';
67

78
import { DashboardDefinition, CallbackAction, UrlAction, Submenu } from '$lib';
89
import { carbon } from '$lib/themes/svelte';
910

1011
import fr from './translations/fr';
1112
import { bookCrud } from './BookCrud';
13+
import { authorCrud } from './AuthorCrud';
1214
import { testCrud } from './TestCrud';
1315
import { ThemeChangerAction } from '$lib/themes/svelte/carbon';
1416

@@ -37,6 +39,7 @@ export const dashboard = new DashboardDefinition({
3739
sideMenu: [
3840
new UrlAction('Homepage', '/', Home),
3941
new UrlAction('Books', '/admin/books/list', Book),
42+
new UrlAction('Authors', '/admin/authors/list', User),
4043
new CallbackAction('Callback link', null, () => {
4144
alert('Hey, this link is called with Javascript!');
4245
}),
@@ -64,5 +67,5 @@ export const dashboard = new DashboardDefinition({
6467
localeDictionaries: {
6568
fr: fr
6669
},
67-
cruds: [bookCrud, testCrud]
70+
cruds: [bookCrud, authorCrud, testCrud]
6871
});
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { faker } from '@faker-js/faker';
2+
import { InMemoryStorage } from './memoryStorage';
3+
4+
let storage: null | InMemoryStorage<Author> = null;
5+
6+
export type Author = {
7+
id: number | string;
8+
first_name: string;
9+
last_name: string;
10+
bio: string;
11+
};
12+
13+
export function getStorage(): InMemoryStorage<Author> {
14+
if (!storage) {
15+
storage = new InMemoryStorage<Author>('Author', getBase);
16+
}
17+
18+
return storage;
19+
}
20+
21+
function getBase(): Array<Author> {
22+
return Array(25)
23+
.fill(undefined)
24+
.map(() => {
25+
return {
26+
id: faker.string.uuid(),
27+
first_name: faker.person.firstName(),
28+
last_name: faker.person.lastName(),
29+
bio: faker.person.bio()
30+
};
31+
});
32+
}

0 commit comments

Comments
 (0)