-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Refactor Voter application layout #619
base: main
Are you sure you want to change the base?
Conversation
frontend/src/lib/api/dataProvider/strapi/strapiDataProvider.type.ts
Outdated
Show resolved
Hide resolved
}); | ||
setPageStylesContext(); | ||
|
||
$: if ($navigating) resetPageStylesContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remember we discussed how to change CSS of a parent from a child. There were two options, use an advanced :has
CSS selector to modify drawer
styles from within candidate
page or setup a global context with CSS settings. The problem is that the context needs to be "reset" to default values once we left that candidate
page. So this line takes care of it in a centralized way. The alternative was to call reset
on the context in every page (which would be unnecessarily boilerplayty in this case) or use onDestroy
on that page, but it is not guaranteed to be called always (as far as I understood) so here we go.
This whole thing is ofc necessary because we use downstream layout
-> page
setup instead of building pages from "lego-blocks" such as components
and slots
(where a new CSS value could be just passed as a property). Hopefully something in Svelte 5 can provide a better solution for us 🤞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me it seems onDestroy
is called rather consistently, but did you find some info stating otherwise? If we can leverage that, we could use it pretty easily make all page-style and header changes specific to the scope (with support for both layouts and pages).
One possibility would be even to make passing the component’s onDestroy
handler a prerequisite for accessing the context (I think it'd be best to combine the various contexts into one anyway), e.g.:
// In app.context.ts
// This would be a bit more involved but it'd make life easy on the consumer side
// Push any merged styles to this stack
const styleStack: Array<PageStyles> = [ DEFAULT_SETTINGS ];
// Merge styles and add to stack
function setPageStyles(styles: DeepPartial<PageStyles> = {}): void {
const current = styleStack[styleStack.length - 1];
const newStyle = lodash.merge(lodash.cloneDeep(current), lodash.cloneDeep(styles));
styleStack.push(newStyle);
// Apply the style here
}
// Use this to move back to a previous state, unless an earlier layout was already unmounted an we already spliced the stack
function resetStack(index: number): void {
if (styleStack.length < index + 1) return;
styleStack.splice(index + 1);
// Apply the style at styleStack[index]
}
// The onDestroy is used to reset the stack to where it was
export function getAppContext(onDestroy: (fn: () => unknown) => void): AppContext {
const currentIndex = styleStack.length - 1;
onDestroy(() => resetStack(currentIndex));
return { setPageStyles }; // And other functions, stores etc.
}
// On a consuming +page/layout.svelte
import { onDestroy } from 'svelte';
import { getAppContext } from '$lib/contexts/app';
const { setPageStyles } = getAppContext(onDestroy);
setPageStyles({ drawer: { background: 'bg-base-300' } });
// These will reset to the previous value when unmounted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now looking at it again, onDestroy should actually work in all cases we may need it, I initially dismissed it more for the reason of being page specific, wanted a centralized layout-based way to reset the CSS but the idea to pass it as a callback would cover that 👍 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I am confused. I am not sure what you have in mind here (even though you brought up an example). Let's discuss and see if maybe you making the change in a separate commit is more efficient than me trying to adapt your vision :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi! I know it's still WIP but already added some preliminary thoughts, but we can go through these on Mon. Looks good anyway!
frontend/src/lib/api/dataProvider/strapi/strapiDataProvider.type.ts
Outdated
Show resolved
Hide resolved
frontend/src/lib/api/dataProvider/strapi/strapiDataProvider.type.ts
Outdated
Show resolved
Hide resolved
<Button href={$getRoute(Route.Help)} variant="icon" icon="help" text={$t('help.title')} /> | ||
{/if} | ||
|
||
{#if $settings.questions.showResultsLink && !$topBarActions.hideResultsButton} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd actually only want to show this on the /questions
route. Perhaps we need to consider a different schema for defining which buttons to show. Maybe smth in the style such that if the value for an action is not defined, the default from settings will be used.
type TopBarActionsSettings = {
[action: TopBarAction]?: 'hide' | 'show';
}
type TopBarAction = 'return' | 'help' | 'feedback'; // etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not actually sure. So we have internal app "state management", which basically makes sure that on certain pages we need to support "showing" something. If the state is set to "show" - then we need to check user settings to see if they want the element to be there or not. I think it is an "AND" relationship here.
I only added the types but they are not partial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might well be a better way than that I proposed, but it would be nice to be able to do these things:
- Show feedback and help buttons on all routes if the settings say so
- Hide either feedback or help by any layout or page
- Show the results button on all
/questions
routes (i.e. defined by a layout) if the settings say so - Show the logout button in the Candidate App only, i.e. on all
/candidate
routes
Then we could do the following:
Banner
doesn't show any actions by default/+layout.svelte
sets the top bar actions:{ help: $settings.header.showHelp ? 'show' : undefined, feedback: $settings.header.showFeedback ? 'show' : undefined }
/questions/+layout.svelte
sets:{ results: $settings.questions.showResults ? 'show' : undefined }
/results/[entityType]/[entityId]/+page.svelte
sets:{ help: 'hide', feedback: 'hide', return: 'show' }
/candidate/+layout.svelte
sets:{ logout: 'show' }
}); | ||
setPageStylesContext(); | ||
|
||
$: if ($navigating) resetPageStylesContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me it seems onDestroy
is called rather consistently, but did you find some info stating otherwise? If we can leverage that, we could use it pretty easily make all page-style and header changes specific to the scope (with support for both layouts and pages).
One possibility would be even to make passing the component’s onDestroy
handler a prerequisite for accessing the context (I think it'd be best to combine the various contexts into one anyway), e.g.:
// In app.context.ts
// This would be a bit more involved but it'd make life easy on the consumer side
// Push any merged styles to this stack
const styleStack: Array<PageStyles> = [ DEFAULT_SETTINGS ];
// Merge styles and add to stack
function setPageStyles(styles: DeepPartial<PageStyles> = {}): void {
const current = styleStack[styleStack.length - 1];
const newStyle = lodash.merge(lodash.cloneDeep(current), lodash.cloneDeep(styles));
styleStack.push(newStyle);
// Apply the style here
}
// Use this to move back to a previous state, unless an earlier layout was already unmounted an we already spliced the stack
function resetStack(index: number): void {
if (styleStack.length < index + 1) return;
styleStack.splice(index + 1);
// Apply the style at styleStack[index]
}
// The onDestroy is used to reset the stack to where it was
export function getAppContext(onDestroy: (fn: () => unknown) => void): AppContext {
const currentIndex = styleStack.length - 1;
onDestroy(() => resetStack(currentIndex));
return { setPageStyles }; // And other functions, stores etc.
}
// On a consuming +page/layout.svelte
import { onDestroy } from 'svelte';
import { getAppContext } from '$lib/contexts/app';
const { setPageStyles } = getAppContext(onDestroy);
setPageStyles({ drawer: { background: 'bg-base-300' } });
// These will reset to the previous value when unmounted
|
||
export let data; | ||
|
||
$: data.opinionQuestions.then((qs) => setProgressBarMax(qs.length + 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NB. We need to use the total number of questions in the selected categories for the max value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The progress bar includes all questions from all categories as steps, it pauses on the category page and then proceeds. At least this is how it worked before the refactoring, basically this preserves the behaviour. :)
Or do you mean that we want to change that to indicate how many left in the current category and then reset to 0 and so no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference is that currently we use the sum of the questions in the categories the user has selected for answering (on /questions/+page.svelte
) instead of all questions. The list of selected questions is calculated on /[questionId]/+page.svelte: ~87
:
$: Promise.all([$opinionQuestions, $opinionQuestionCategories]).then(
([qq]) => (questions = filterAndSortQuestions(qq, $firstQuestionId, $selectedCategories))
);
And questions.length
used for the total.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not getting what's wrong with this logic. Sorry :D Let's discuss it on Monday.
a9bd0cd
to
0fa7a6a
Compare
Removes the ACL populate restrictions from `App Settings` and `App Customization` content types. They are not needed for security, because the types do not contain relations. Having them needlessly complicates adding new settings, because each component needs to be explicitly allowed to be populated.
0fa7a6a
to
16abbfe
Compare
Add proper typing to the Strapi functions handling `DynamicSettings` and add null checks to prevent errors if the initial `App Settings` object was not created correctly.
A PoC of the stacked context paradigm where any edits are reset by the consuming component’s `onDestroy` lifecycle hook. The example sets the page background using both `+page`s and `+layout`s: 1. /(voters)/+layout: init the context with default bg-base-100 2. /(voters)/+page: base-300 for the front page 3. /questions/+layout: base-300 for the whole questions route 4. /questions/[categoryId]/+page base-100 for just the category intro pages 5. /(voters)/results/[entityType]/[entityId]/+page base-300 for the entity detail page NB. Ultimately, we would init the context in `/+layout` but we're making changes now only to the Voter App.
] | ||
); | ||
// We can add more reversion actions here when needed | ||
const revert = (index: number) => pageStyles.revert(index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But when we do, we just need to either use one stack with a more complex type for all changes or enforce consistent indexing between the stacks.
WHY:
Switching to a more conventional Svelte "layout -> page" model.
This allows us to:
TODO:
Page
propertiesWhat has been changed (if possible, add screenshots, gifs, etc. )
Check off each of the following tasks as they are completed
Clean up your git commit history before submitting the pull request!