Skip to content

Commit

Permalink
Merge branch 'develop' into gsoc-table-develop
Browse files Browse the repository at this point in the history
  • Loading branch information
BabyElias authored Aug 14, 2024
2 parents 2e45abe + ce9e707 commit 5084f30
Show file tree
Hide file tree
Showing 14 changed files with 443 additions and 21 deletions.
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,38 @@ Changelog is rather internal in nature. See release notes for the public overvie
- **Impacts a11y:** Yes
- **Guidance:**
[#727] https://github.com/learningequality/kolibri-design-system/pull/727

- [#718]
- **Description:** This pull request resolves failing `KDateCalendar` component tests that occurred on the last day of the month in open pull requests by setting dates manually in the tests. Additionally, the `KDateCalendar` is updated to show the month of the `lastAllowedDate` property.
- **Products impact:** none
- **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/713
- **Components:** -
- **Breaking:** no
- **Impacts a11y:** -
- **Guidance:** -

[#718]: https://github.com/learningequality/kolibri-design-system/pull/718

- [#687]
- **Description:** Adds logic that inserts ARIA live assertive and polite regions to an application's document body during KDS initialization and documents this on the new "Installation" page. Relatedly adds `useKLiveRegion` composable with public methods for updating the live regions with assertive and polite messages.
- **Products impact:** new API
- **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/668
- **Components:** `useKLiveRegion`
- **Breaking:** No
- **Impacts a11y:** Yes. It will fix several places utilizing live regions that don't work in our applications at all. Furthemore, it follows the recommended practices that will fix major a11y issues with live regions we're having.
- **Guidance:** Find all polite and live regions (or roles) in an application. Remove them and instead use `useKLiveRegion.sendPoliteMessage` and `useKLiveRegion.sendAssertiveMessage` to update the live regions that KDS inserted to document body during installation.

[#687]: https://github.com/learningequality/kolibri-design-system/pull/687
[#688]
- **Description:** Update`KCard` styling
- **Products impact:** Card updates
- **Addresses:**
- **Components:** `KCard`
- **Breaking:** N0
- **Impacts a11y:** No
- **Guidance:**
[#688] https://github.com/learningequality/kolibri-design-system/pull/688

[#707]
- **Description:** Card Validations
- **Products impact:**
Expand Down
5 changes: 5 additions & 0 deletions docs/assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,8 @@ dt {
dd {
margin-left: 20px;
}

em {
font-style: normal;
font-weight: bold;
}
40 changes: 40 additions & 0 deletions docs/pages/installation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>

<DocsPageTemplate>

<DocsPageSection title="1. Install the plugin" anchor="#install-plugin">
<p>Kolibri Design System (KDS) is a Vue plugin. Register it in your application alongside scripts that are needed for its full functioning:</p>

<!-- eslint-disable -->
<DocsShowCode language="javascript">
import KThemePlugin from 'kolibri-design-system/lib/KThemePlugin';
import trackInputModality from 'kolibri-design-system/lib/styles/trackInputModality';
import trackMediaType from 'kolibri-design-system/lib/styles/trackMediaType';

Vue.use(KThemePlugin);

trackInputModality();
trackMediaType();
</DocsShowCode>
<!-- eslint-enable -->

This ensures the following:

<ul>
<li>Installs <code>$themeBrand</code>, <code>$themeTokens</code> <code>$themePalette</code>, and <code>$computedClass</code> helpers on all Vue instances (see <DocsInternalLink href="/colors/#usage" text="Colors" />).</li>
<li>Provides <code>$coreOutline</code>, <code>$inputModality</code>, <code>$mediaType</code>, and <code>$isPrint</code> computed properties as well as <code>$print</code> method to all Vue instances.</li>
<li>Globally registers all KDS Vue components.</li>
<li>Inserts assertive and polite ARIA live regions to your application's document body (see <DocsInternalLink href="/usekliveregion" text="useKLiveRegion" />).</li>
</ul>
</DocsPageSection>

<DocsPageSection title="2. Initialize theme" anchor="#initialize-theme">
<p>
Until this section is better documented, refer to <DocsExternalLink href="https://github.com/learningequality/kolibri/blob/develop/kolibri/core/assets/src/styles/initializeTheme.js" text="Kolibri's initializeTheme.js" />.
</p>
</DocsPageSection>

</DocsPageTemplate>

</template>

10 changes: 0 additions & 10 deletions docs/pages/kimg.vue
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,3 @@
export default {};
</script>


<style lang="scss" scoped>
em {
font-style: normal;
font-weight: bold;
}
</style>
123 changes: 123 additions & 0 deletions docs/pages/usekliveregion.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<template>

<DocsPageTemplate apiDocs>

<DocsPageSection title="Overview" anchor="#overview">
<p>A composable that offers <code>sendPoliteMessage</code> and <code>sendAssertiveMessage</code> functions that send polite and assertive messages to their corresponding <DocsExternalLink href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions" text="ARIA live regions" />.</p>
</DocsPageSection>

<DocsPageSection title="When to use live regions" anchor="#usage">
<p>Before sending messages to live regions, always research carefully if you really need it for the task ahead. Live regions can be <DocsExternalLink href="https://www.sarasoueidan.com/blog/accessible-notifications-with-aria-live-regions-part-2/#avoid-live-regions-if-you-can" text="buggy and inconsistent" />. There are often better alternatives, such as utilizing <DocsExternalLink href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes" text="WAI-ARIA attributes" />. A good rule of thumb is to use live regions only when there's no other way.</p>
</DocsPageSection>

<DocsPageSection title="Usage" anchor="#usage">
<p>Since polite and assertive regions are inserted to an application's document body automatically <DocsInternalLink href="/installation#install-plugin" text="during the KDS installation process" />, the only thing you need to do to deliver messages is to import and call <code>sendPoliteMessage</code> or <code>sendAssertiveMessage</code> from any place in your application.</p>

<p>These two methods are also used internally from some KDS components to provide a11y out of the box. Always check that you don't send messages to announce updates that are already being announced from KDS to prevent from duplicate announcements.</p>

<h3>Polite message</h3>

<p>Sending a polite message updates the text content of <code>aria-live="polite"</code> region. <em>Use it to send messages that can wait to be announced until the user is idle. This message should typically be the most commonly used.</em></p>

<p>Send polite messages with <code>sendPoliteMessage(message)</code>:</p>

<!-- eslint-disable -->
<!-- prevent prettier from changing indentation -->
<DocsShowCode language="javascript">
import useKLiveRegion from 'kolibri-design-system/lib/composables/useKLiveRegion';

export default {
setup() {
const { sendPoliteMessage } = useKLiveRegion();
sendPoliteMessage('Polite message');
}
};
</DocsShowCode>
<!-- eslint-enable -->

<h3>Assertive message</h3>

<p>Sending an assertive message updates the text content of <code>aria-live="assertive"</code> region. <em>It should be used with caution because it disrupts the user's flow. Use it only to send messages that require immediate attention, such as errors.</em></p>

<p>Send assertive messages with <code>sendAssertiveMessage(message)</code>:</p>

<!-- eslint-disable -->
<!-- prevent prettier from changing indentation -->
<DocsShowCode language="javascript">
import useKLiveRegion from 'kolibri-design-system/lib/composables/useKLiveRegion';

export default {
setup() {
const { sendAssertiveMessage } = useKLiveRegion();
sendPoliteMessage('Assertive message');
}
};
</DocsShowCode>
<!-- eslint-enable -->
</DocsPageSection>

<DocsPageSection title="Demo" anchor="#demo">
<p>Send messages below and turn on your screen reader. You could also observe the content of <code>&lt;div id="k-live-region"&gt;</code> in the browser console, but note that an announcement will be visible for just a very brief moment.</p>

<DocsShow language="html">
<KTextbox label="Polite message" :value="politeMessageInput" @input="updatePoliteMessage" />
<KButton @click="sendPoliteMessage(politeMessageInput)">
Send
</KButton>
</DocsShow>

<DocsShow language="html">
<KTextbox label="Assertive message" :value="assertiveMessageInput" @input="updateAssertiveMessage" />
<KButton @click="sendAssertiveMessage(assertiveMessageInput)">
Send
</KButton>
</DocsShow>
</DocsPageSection>

<DocsPageSection title="Related" anchor="#related">
<ul>
<li>
<DocsInternalLink href="/installation#install-plugin" text="KDS installation step" /> that attaches live regions to an application's DOM
</li>
</ul>
</DocsPageSection>
</DocsPageTemplate>

</template>


<script>
import { ref } from '@vue/composition-api';
import useKLiveRegion from '../../lib/composables/useKLiveRegion';
export default {
setup() {
const { _mountLiveRegion, sendPoliteMessage, sendAssertiveMessage } = useKLiveRegion();
const politeMessageInput = ref('Polite hello');
const updatePoliteMessage = message => {
politeMessageInput.value = message;
};
const assertiveMessageInput = ref('I cannot wait');
const updateAssertiveMessage = message => {
assertiveMessageInput.value = message;
};
return {
_mountLiveRegion,
updatePoliteMessage,
politeMessageInput,
updateAssertiveMessage,
assertiveMessageInput,
sendPoliteMessage,
sendAssertiveMessage,
};
},
mounted() {
this._mountLiveRegion(this.$root.$el);
},
};
</script>
19 changes: 19 additions & 0 deletions docs/tableOfContents.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ export default [
path: '/',
title: 'Home',
}),
new Page({
path: '/installation',
title: 'Installation',
}),
new Page({
path: '/principles',
title: 'Design principles',
Expand Down Expand Up @@ -180,6 +184,21 @@ export default [
isCode: true,
keywords: [...compositionRelatedKeywords, 'responsive', 'window', 'breakpoint'],
}),
new Page({
path: '/usekliveregion',
title: 'useKLiveRegion',
isCode: true,
keywords: [
...compositionRelatedKeywords,
'a11y',
'live',
'region',
'aria',
'polite',
'assertive',
'message',
],
}),
new Page({
path: '/usekshow',
title: 'useKShow',
Expand Down
28 changes: 28 additions & 0 deletions jest.conf/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,31 @@ global.flushPromises = function flushPromises() {
setImmediate(resolve);
});
};

function removeWhitespaceFromHtml(htmlString) {
// https://stackoverflow.com/a/33108909
return htmlString.replace(/>\s+|\s+</g, function(m) {
return m.trim();
});
}

global.expect.extend({
// check that document.body.innerHTML includes html string

toBeInDom(received) {
const pass = removeWhitespaceFromHtml(document.body.innerHTML).includes(
removeWhitespaceFromHtml(received)
);
if (pass) {
return {
message: () => `expected ${received} not to be in the document body`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to be in the document body`,
pass: false,
};
}
},
});
4 changes: 2 additions & 2 deletions lib/KCard/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,8 @@
.below-title {
order: 4;
height: 18px; /* (1) */
min-height: 18px; /* (2) */
height: 26px; /* (1) */
min-height: 26px; /* (2) */
margin: 0 $spacer $spacer;
overflow: hidden; /* (1) */
}
Expand Down
5 changes: 3 additions & 2 deletions lib/KDateRange/KDateCalendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,9 @@
},
numOfDays: 7,
isFirstChoice: this.selectedStartDate == null ? true : false,
activeMonth: new Date().getMonth() - 1 == -1 ? 11 : new Date().getMonth() - 1,
activeYearStart: new Date().getFullYear(),
activeMonth:
this.lastAllowedDate.getMonth() - 1 == -1 ? 11 : this.lastAllowedDate.getMonth() - 1,
activeYearStart: this.lastAllowedDate.getFullYear(),
};
},
computed: {
Expand Down
15 changes: 9 additions & 6 deletions lib/KDateRange/__tests__/KDateRange.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ describe('KDateRange component', () => {

const KDATECALENDAR_PROPS = {
firstAllowedDate: new Date(2022, 0, 1),
lastAllowedDate: new Date(),
lastAllowedDate: new Date(2022, 10, 1),
selectedStartDate: new Date(2022, 8, 1),
selectedEndDate: new Date(),
selectedEndDate: new Date(2022, 10, 1),
previousMonthText: 'Previous Month',
nextMonthText: 'Next Month',
};
Expand Down Expand Up @@ -120,16 +120,19 @@ describe('KDateCalendar component', () => {
expect(wrapper.exists()).toBe(true);
});

it('the month and year display on the left-side calendar view should contain the previous month', async () => {
it('the month and year display on the left-side calendar view should display the previous month of the lastAllowedDate', async () => {
const previousMonthDisplay = getKDateCalendarElements(wrapper).previousMonthDisplay();
const previousMonth = new Date().setDate(0);
const lastAllowedDate = wrapper.props().lastAllowedDate;
const previousMonth = new Date(lastAllowedDate);
previousMonth.setMonth(lastAllowedDate.getMonth() - 1);
const date = Vue.prototype.$formatDate(previousMonth, { month: 'long', year: 'numeric' });
expect(previousMonthDisplay.text()).toBe(date);
});

it('the month and year display on the right-side calendar view should contain the current month', async () => {
it('the month and year display on the right-side calendar view should display the current month of the lastAllowedDate', async () => {
const currentMonthDisplay = getKDateCalendarElements(wrapper).currentMonthDisplay();
const date = Vue.prototype.$formatDate(new Date(), { month: 'long', year: 'numeric' });
const lastAllowedDate = wrapper.props().lastAllowedDate;
const date = Vue.prototype.$formatDate(lastAllowedDate, { month: 'long', year: 'numeric' });
expect(currentMonthDisplay.text()).toBe(date);
});
});
22 changes: 22 additions & 0 deletions lib/KThemePlugin.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isNuxtServerSideRendering } from '../lib/utils';
import computedClass from './styles/computedClass';

import KBreadcrumbs from './KBreadcrumbs';
Expand Down Expand Up @@ -40,13 +41,34 @@ import KCard from './KCard';
import { themeTokens, themeBrand, themePalette, themeOutlineStyle } from './styles/theme';
import globalThemeState from './styles/globalThemeState';

import useKLiveRegion from './composables/useKLiveRegion';

const { _mountLiveRegion } = useKLiveRegion();

require('./grids/globalStyles.js'); // global grid styles

/**
* Install Kolibri theme helpers on all Vue instances.
* Also, set up global state, listeners, and styles.
*/
export default function KThemePlugin(Vue) {
// Note that if DOM live regions need to be demostrated
// on the KDS website, and therefore attached to the DOM,
// just call _mountLiveRegion() in the relevant documentation
// page's 'mounted' (see 'docs/pages/usekliveregio.vue' for an example)
if (!isNuxtServerSideRendering()) {
const onDomReady = () => {
_mountLiveRegion();
document.removeEventListener('DOMContentLoaded', onDomReady);
};

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', onDomReady);
} else {
onDomReady();
}
}

Vue.mixin({
/* eslint-disable kolibri/vue-no-unused-properties */
computed: {
Expand Down
Loading

0 comments on commit 5084f30

Please sign in to comment.