diff --git a/static/js/src/advantage/credentials/components/CredManage/CredManage.tsx b/static/js/src/advantage/credentials/components/CredManage/CredManage.tsx index 1ccbb114fb3..7bef3c8abbe 100644 --- a/static/js/src/advantage/credentials/components/CredManage/CredManage.tsx +++ b/static/js/src/advantage/credentials/components/CredManage/CredManage.tsx @@ -17,6 +17,15 @@ type ActivationKey = { activatedBy?: string; productID: string; }; + +enum KeyFilters { + All, + Unused, + Active, +} + +const KeyFiltersLength = Object.keys(KeyFilters).length / 2; + const CredManage = () => { const [tab, changeTab] = useState(0); const inputRefs = useRef([]); @@ -57,12 +66,12 @@ const CredManage = () => { ) => { event.preventDefault(); if (event.key == "ArrowLeft") { - changeTab((currentIndex - 1) % 4); - inputRefs.current[(currentIndex - 1) % 4].focus(); + changeTab((currentIndex - 1) % KeyFiltersLength); + inputRefs.current[(currentIndex - 1) % KeyFiltersLength].focus(); } if (event.key == "ArrowRight") { - changeTab((currentIndex + 1) % 4); - inputRefs.current[(currentIndex + 1) % 4].focus(); + changeTab((currentIndex + 1) % KeyFiltersLength); + inputRefs.current[(currentIndex + 1) % KeyFiltersLength].focus(); } }; @@ -103,23 +112,6 @@ const CredManage = () => { navigator.clipboard.writeText(value); }; - const keyIsArchivable = (keyItemId: string) => { - const tempKey = getKey(keyItemId); - if ("activatedBy" in tempKey) { - return true; - } - return false; - }; - - const isArchiveable = () => { - for (let i = 0; i < selectedKeyIds.length; i++) { - if (!keyIsArchivable(selectedKeyIds[i])) { - return false; - } - } - return true; - }; - const keyIsUnused = (keyItemId: string) => { const tempKey = getKey(keyItemId); if ("activatedBy" in tempKey) { @@ -139,12 +131,6 @@ const CredManage = () => { useEffect(() => { const newList = []; - if (isArchiveable()) { - newList.push({ - children: "Archive", - onClick: () => {}, - }); - } if (isUnused()) { newList.push({ children: "Copy List", @@ -167,24 +153,23 @@ const CredManage = () => { }); } updateActionLinks(newList); - console.log(isArchiveable()); }, [selectedKeyIds]); useEffect(() => { - if (tab == 0) { + if (tab == KeyFilters.All) { changeTableData(filterData); } - if (tab == 1) { + if (tab == KeyFilters.Unused) { changeTableData( filterData.filter((keyItem: ActivationKey) => { return keyIsUnused(keyItem["key"]); }) ); } - if (tab == 2) { + if (tab == KeyFilters.Active) { changeTableData( filterData.filter((keyItem: ActivationKey) => { - return !keyIsArchivable(keyItem["key"]); + return !keyIsUnused(keyItem["key"]); }) ); } @@ -223,7 +208,7 @@ const CredManage = () => { changeTab(0); }} onKeyUp={(e) => { - switchTab(e, 4); + switchTab(e, KeyFiltersLength); }} ref={(ref: HTMLButtonElement) => (inputRefs.current[0] = ref)} > @@ -263,23 +248,6 @@ const CredManage = () => { > Active Keys -
@@ -375,14 +343,7 @@ const CredManage = () => { content: ( <> {keyitem["activatedBy"] ? ( - -

- {keyitem["activatedBy"]}   - {}}> - - -

-
+

{keyitem["activatedBy"]}  

) : (

diff --git a/static/js/src/advantage/subscribe/checkout/components/BuyButton/BuyButton.tsx b/static/js/src/advantage/subscribe/checkout/components/BuyButton/BuyButton.tsx index cc3665eb23c..fbecd056aaa 100644 --- a/static/js/src/advantage/subscribe/checkout/components/BuyButton/BuyButton.tsx +++ b/static/js/src/advantage/subscribe/checkout/components/BuyButton/BuyButton.tsx @@ -11,6 +11,7 @@ import useCustomerInfo from "../../hooks/useCustomerInfo"; import useFinishPurchase from "../../hooks/useFinishPurchase"; import usePollPurchaseStatus from "../../hooks/usePollPurchaseStatus"; import { Action, FormValues, Product } from "../../utils/types"; +import { currencyFormatter } from "advantage/react/utils"; type Props = { setError: React.Dispatch>; @@ -101,6 +102,19 @@ const BuyButton = ({ setError, quantity, product, action }: Props) => { setPendingPurchaseID(purchaseId); window.currentPaymentId = purchaseId; } + + window.plausible("pro-purchase", { + props: { + country: values.country, + product: product?.name, + quantity: quantity, + total: + values.totalPrice && + currencyFormatter.format(values?.totalPrice / 100), + "buying-for": values.buyingFor, + action: buyAction, + }, + }); }, onError: (error) => { setIsLoading(false); @@ -250,10 +264,34 @@ const BuyButton = ({ setError, quantity, product, action }: Props) => { proSelectorStates.forEach((state) => localStorage.removeItem(state)); + const address = userInfo + ? `${userInfo?.customerInfo?.address?.line1} ${userInfo?.customerInfo?.address?.line2} ${userInfo?.customerInfo?.address?.city} ${userInfo?.customerInfo?.address?.postal_code} ${userInfo?.customerInfo?.address?.state} ${userInfo?.customerInfo?.address?.country}` + : `${values?.address} ${values?.postalCode} ${values?.city} ${values?.usState} ${values?.caProvince} ${values?.country}`; + + const getName = () => { + const name = userInfo?.customerInfo?.name || values?.name; + if (name && name.split(" ").length === 2) { + formData.append("firstName", name.split(" ")[0] ?? ""); + formData.append("lastName", name.split(" ")[1] ?? ""); + } else { + formData.append("firstName", ""); + formData.append("lastName", name ?? ""); + } + }; + const request = new XMLHttpRequest(); const formData = new FormData(); formData.append("formid", "3756"); - formData.append("email", userInfo?.customerInfo?.email ?? ""); + getName(); + formData.append( + "email", + (userInfo?.customerInfo?.email || values.email) ?? "" + ); + formData.append( + "company", + (userInfo?.accountInfo?.name || values?.organisationName) ?? "" + ); + formData.append("street", address ?? ""); formData.append("Consent_to_Processing__c", "yes"); formData.append("GCLID__c", sessionData?.gclid || ""); formData.append("utm_campaign", sessionData?.utm_campaign || ""); diff --git a/static/js/src/advantage/subscribe/checkout/hooks/useCalculate.tsx b/static/js/src/advantage/subscribe/checkout/hooks/useCalculate.tsx index 7ed8146fc45..db3ddfa5012 100644 --- a/static/js/src/advantage/subscribe/checkout/hooks/useCalculate.tsx +++ b/static/js/src/advantage/subscribe/checkout/hooks/useCalculate.tsx @@ -1,6 +1,7 @@ import { useQuery } from "react-query"; import { UserSubscriptionMarketplace } from "advantage/api/enum"; -import { TaxInfo } from "../utils/types"; +import { FormValues, TaxInfo } from "../utils/types"; +import { useFormikContext } from "formik"; type useCalculateProps = { marketplace: UserSubscriptionMarketplace; @@ -19,6 +20,7 @@ const useCalculate = ({ VATNumber, isTaxSaved, }: useCalculateProps) => { + const { setFieldValue } = useFormikContext(); const { isLoading, isError, isSuccess, data, error, isFetching } = useQuery( ["calculate"], async () => { @@ -52,7 +54,7 @@ const useCalculate = ({ } const data: TaxInfo = res; - + setFieldValue("totalPrice", data.total); return data; }, { diff --git a/static/js/src/advantage/subscribe/checkout/hooks/usePreview.tsx b/static/js/src/advantage/subscribe/checkout/hooks/usePreview.tsx index a7bd33ea2ca..5edbff6b6cf 100644 --- a/static/js/src/advantage/subscribe/checkout/hooks/usePreview.tsx +++ b/static/js/src/advantage/subscribe/checkout/hooks/usePreview.tsx @@ -1,6 +1,13 @@ import { useQuery } from "react-query"; -import { Action, PaymentPayload, Product, TaxInfo } from "../utils/types"; +import { + Action, + FormValues, + PaymentPayload, + Product, + TaxInfo, +} from "../utils/types"; import useCustomerInfo from "./useCustomerInfo"; +import { useFormikContext } from "formik"; type Props = { quantity: number; @@ -10,7 +17,7 @@ type Props = { const usePreview = ({ quantity, product, action }: Props) => { const { isError: isUserInfoError } = useCustomerInfo(); - + const { setFieldValue } = useFormikContext(); const { isLoading, isError, isSuccess, data, error, isFetching } = useQuery( ["preview", product], async () => { @@ -70,6 +77,7 @@ const usePreview = ({ quantity, product, action }: Props) => { end_of_cycle: res.end_of_cycle, }; + setFieldValue("totalPrice", data.total); return data; }, { diff --git a/static/js/src/advantage/subscribe/checkout/utils/helpers.ts b/static/js/src/advantage/subscribe/checkout/utils/helpers.ts index 484ef57b9df..c2ceea0a0f9 100644 --- a/static/js/src/advantage/subscribe/checkout/utils/helpers.ts +++ b/static/js/src/advantage/subscribe/checkout/utils/helpers.ts @@ -32,6 +32,7 @@ export function getInitialFormValues( isTaxSaved: !!userInfo?.customerInfo?.address?.country, isCardValid: !!userInfo?.customerInfo?.defaultPaymentMethod, isInfoSaved: !!userInfo?.customerInfo?.defaultPaymentMethod, + totalPrice: undefined, }; } diff --git a/static/js/src/advantage/subscribe/checkout/utils/types.ts b/static/js/src/advantage/subscribe/checkout/utils/types.ts index 8ca94fc9ff0..f2619bcd7fc 100644 --- a/static/js/src/advantage/subscribe/checkout/utils/types.ts +++ b/static/js/src/advantage/subscribe/checkout/utils/types.ts @@ -52,6 +52,7 @@ export interface FormValues { isTaxSaved: boolean; isCardValid: boolean; isInfoSaved: boolean; + totalPrice: undefined | number; } export type marketplace = "canonical-ua" | "canonical-cube" | "blender"; diff --git a/static/js/src/chart-data.js b/static/js/src/chart-data.js index 41edd0431b6..8cec1ad0e09 100644 --- a/static/js/src/chart-data.js +++ b/static/js/src/chart-data.js @@ -137,6 +137,27 @@ export var serverAndDesktopReleases = [ ]; export var kernelReleases = [ + { + startDate: new Date("2023-02-01T00:00:00"), + endDate: new Date("2027-04-01T00:00:00"), + taskName: "Ubuntu 22.04.2 LTS", + taskVersion: "Kernel version", + status: "LTS", + }, + { + startDate: new Date("2022-10-01T00:00:00"), + endDate: new Date("2023-07-01T00:00:00"), + taskName: "Ubuntu 22.10", + taskVersion: "", + status: "INTERIM_RELEASE", + }, + { + startDate: new Date("2022-08-01T00:00:00"), + endDate: new Date("2027-04-01T00:00:00"), + taskName: "Ubuntu 22.04.1 LTS", + taskVersion: "", + status: "LTS", + }, { startDate: new Date("2022-08-01T00:00:00"), endDate: new Date("2025-04-30T00:00:00"), @@ -363,6 +384,39 @@ export var kernelReleases = [ }, ]; +export var kernelReleases2204 = [ + { + startDate: new Date("2022-04-23T00:00:00"), + endDate: new Date("2027-04-22T00:00:00"), + taskName: "Ubuntu 22.04.0 LTS (v5.15)", + status: "LTS", + }, + { + startDate: new Date("2027-04-22T00:00:00"), + endDate: new Date("2032-04-20T00:00:00"), + taskName: "Ubuntu 22.04.0 LTS (v5.15)", + status: "ESM", + }, + { + startDate: new Date("2022-08-21T00:00:00"), + endDate: new Date("2027-04-22T00:00:00"), + taskName: "Ubuntu 22.04.1 LTS (v5.15)", + status: "LTS", + }, + { + startDate: new Date("2027-04-22T00:00:00"), + endDate: new Date("2032-04-21T00:00:00"), + taskName: "Ubuntu 22.04.1 LTS (v5.15)", + status: "ESM", + }, + { + startDate: new Date("2023-02-17T00:00:00"), + endDate: new Date("2023-07-22T00:00:00"), + taskName: "Ubuntu 22.04.2 LTS (v5.19)", + status: "LTS", + }, +]; + export var kernelReleases2004 = [ { startDate: new Date("2022-08-11T00:00:00"), @@ -796,6 +850,18 @@ export var kernelReleasesALL = [ taskName: "Ubuntu 20.04.4 LTS (v5.13)", status: "LTS", }, + { + startDate: new Date("2022-01-01T00:00:00"), + endDate: new Date("2022-04-01T00:00:00"), + taskName: "Ubuntu 22.04.0 LTS (v5.15)", + status: "EARLY", + }, + { + startDate: new Date("2022-04-01T00:00:00"), + endDate: new Date("2027-04-01T00:00:00"), + taskName: "Ubuntu 22.04.0 LTS (v5.15)", + status: "LTS", + }, { startDate: new Date("2022-05-13T00:00:00"), endDate: new Date("2022-08-11T00:00:00"), @@ -808,6 +874,18 @@ export var kernelReleasesALL = [ taskName: "Ubuntu 20.04.5 LTS (v5.15)", status: "LTS", }, + { + startDate: new Date("2022-08-01T00:00:00"), + endDate: new Date("2027-04-01T00:00:00"), + taskName: "Ubuntu 22.04.1 LTS (v5.15)", + status: "LTS", + }, + { + startDate: new Date("2023-02-01T00:00:00"), + endDate: new Date("2023-07-01T00:00:00"), + taskName: "Ubuntu 22.04.2 LTS (v5.19)", + status: "LTS", + }, ]; export var kernelReleasesLTS = [ @@ -1021,6 +1099,18 @@ export var kernelReleasesLTS = [ taskName: "Ubuntu 20.04.4 LTS (v5.13)", status: "LTS", }, + { + startDate: new Date("2022-04-01T00:00:00"), + endDate: new Date("2024-04-01T00:00:00"), + taskName: "Ubuntu 22.04.0 LTS (v5.15)", + status: "LTS", + }, + { + startDate: new Date("2024-04-01T00:00:00"), + endDate: new Date("2027-04-01T00:00:00"), + taskName: "Ubuntu 22.04.0 LTS (v5.15)", + status: "CVE", + }, { startDate: new Date("2022-08-11T00:00:00"), endDate: new Date("2024-08-10T00:00:00"), @@ -1033,6 +1123,24 @@ export var kernelReleasesLTS = [ taskName: "Ubuntu 20.04.5 LTS (v5.15)", status: "CVE", }, + { + startDate: new Date("2022-08-01T00:00:00"), + endDate: new Date("2024-04-01T00:00:00"), + taskName: "Ubuntu 22.04.1 LTS (v5.15)", + status: "LTS", + }, + { + startDate: new Date("2024-04-01T00:00:00"), + endDate: new Date("2027-04-01T00:00:00"), + taskName: "Ubuntu 22.04.1 LTS (v5.15)", + status: "CVE", + }, + { + startDate: new Date("2023-02-01T00:00:00"), + endDate: new Date("2023-07-01T00:00:00"), + taskName: "Ubuntu 22.04.2 LTS (v5.19)", + status: "LTS", + }, ]; export var kernelReleaseSchedule = [ @@ -1448,6 +1556,9 @@ export var desktopServerReleaseNames = [ ]; export var kernelReleaseNames = [ + "Ubuntu 22.04.2 LTS", + "Ubuntu 22.10", + "Ubuntu 22.04.1 LTS", "Ubuntu 20.04.5 LTS", "Ubuntu 22.04.0 LTS", "Ubuntu 20.04.4 LTS", @@ -1470,8 +1581,11 @@ export var kernelReleaseNames = [ ]; export var kernelVersionNames = [ + "5.19 kernel", + "", "5.15 kernel", "", + "", "5.13 kernel", "", "5.11 kernel", @@ -1491,6 +1605,12 @@ export var kernelVersionNames = [ "", ]; +export var kernelReleaseNames2204 = [ + "Ubuntu 22.04.0 LTS (v5.15)", + "Ubuntu 22.04.1 LTS (v5.15)", + "Ubuntu 22.04.2 LTS (v5.19)", +]; + export var kernelReleaseNames2004 = [ "Ubuntu 20.04.0 LTS (v5.4)", "Ubuntu 20.04.1 LTS (v5.4)", @@ -1551,7 +1671,10 @@ export var kernelReleaseNamesALL = [ "Ubuntu 20.04.2 LTS (v5.8)", "Ubuntu 20.04.3 LTS (v5.11)", "Ubuntu 20.04.4 LTS (v5.13)", + "Ubuntu 22.04.0 LTS (v5.15)", "Ubuntu 20.04.5 LTS (v5.15)", + "Ubuntu 22.04.1 LTS (v5.15)", + "Ubuntu 22.04.2 LTS (v5.19)", ]; export var kernelReleaseNamesLTS = [ @@ -1578,7 +1701,10 @@ export var kernelReleaseNamesLTS = [ "Ubuntu 20.04.2 LTS (v5.8)", "Ubuntu 20.04.3 LTS (v5.11)", "Ubuntu 20.04.4 LTS (v5.13)", + "Ubuntu 22.04.0 LTS (v5.15)", "Ubuntu 20.04.5 LTS (v5.15)", + "Ubuntu 22.04.1 LTS (v5.15)", + "Ubuntu 22.04.2 LTS (v5.19)", ]; export var openStackReleaseNames = [ diff --git a/static/js/src/chart.js b/static/js/src/chart.js index f8069ea3292..1eefc9880d7 100644 --- a/static/js/src/chart.js +++ b/static/js/src/chart.js @@ -278,12 +278,14 @@ export function createChart( var rowHeight = 32; var timeDomainStart; var timeDomainEnd; + var earliestStartDate = d3.min(tasks, (d) => d.startDate); + var latestEndDate = d3.max(tasks, (d) => d.endDate); if (removePadding) { - timeDomainStart = tasks[0].startDate; - timeDomainEnd = tasks[tasks.length - 1].endDate; + timeDomainStart = earliestStartDate; + timeDomainEnd = latestEndDate; } else { - timeDomainStart = d3.timeYear.offset(tasks[0].startDate, -1); - timeDomainEnd = d3.timeYear.offset(tasks[tasks.length - 1].endDate, +1); + timeDomainStart = d3.timeYear.offset(earliestStartDate, -1); + timeDomainEnd = d3.timeYear.offset(latestEndDate, +1); } var height = taskTypes.length * rowHeight; var containerWidth = document.querySelector(chartSelector).clientWidth; diff --git a/static/js/src/intlTelInput.js b/static/js/src/intlTelInput.js index ad572d5e550..2525c1f80d5 100644 --- a/static/js/src/intlTelInput.js +++ b/static/js/src/intlTelInput.js @@ -25,6 +25,52 @@ function setupIntlTelInput(phoneInput) { success(countryCode); }, }); + + // create error message node + const mobileInput = document.querySelector(".iti"); + mobileInput.parentNode.classList.add("p-form-validation"); + phoneInput.classList.add("p-form-validation__input"); + const errorElement = document.createElement("div"); + errorElement.classList.add("p-form-validation__message"); + errorElement.style.marginTop = "1rem"; + const errorMessage = document.createTextNode("Please enter a valid number."); + errorElement.appendChild(errorMessage); + // accessibility improvements + const countryCodeDropdown = mobileInput.querySelector(".iti__selected-flag"); + countryCodeDropdown.setAttribute("aria-label", "Dial code list"); + phoneInput.setAttribute("aria-describedby", "invalid-number-message"); + errorElement.setAttribute("id", "invalid-number-message"); + errorElement.setAttribute("role", "alert"); + + function reset() { + mobileInput.parentNode.classList.remove("is-error"); + errorElement.remove(); + } + + function isValidNumber(number) { + const pattern = /^(?=[^a-zA-Z]*$)[0-9\s.\-()/,]{4,25}$/; + return pattern.test(number); + } + + // on blur: validate + phoneInput.addEventListener("blur", function () { + reset(); + if (phoneInput.value.trim()) { + if (isValidNumber(phoneInput.value.trim())) { + reset(); + } else { + mobileInput.parentNode.classList.add("is-error"); + mobileInput.parentNode.insertBefore( + errorElement, + mobileInput.nextSibling + ); + } + } + }); + + // on keyup / change flag: reset + phoneInput.addEventListener("change", reset); + phoneInput.addEventListener("keyup", reset); } const targetPhoneInput = document.querySelector("input#phone"); diff --git a/static/js/src/release-chart.js b/static/js/src/release-chart.js index e4ddf129343..6f1b81767ef 100644 --- a/static/js/src/release-chart.js +++ b/static/js/src/release-chart.js @@ -5,6 +5,7 @@ import { serverAndDesktopReleases, kernelReleases, kernelReleaseSchedule, + kernelReleases2204, kernelReleases2004, kernelReleases1804, kernelReleases1604, @@ -25,6 +26,7 @@ import { kernelReleaseNames, kernelVersionNames, kernelReleaseScheduleNames, + kernelReleaseNames2204, kernelReleaseNames2004, kernelReleaseNames1804, kernelReleaseNames1604, @@ -74,6 +76,14 @@ function buildCharts() { kernelVersionNames ); } + if (document.querySelector("#kernel2204")) { + createChart( + "#kernel2204", + kernelReleaseNames2204, + kernelStatus, + kernelReleases2204 + ); + } if (document.querySelector("#kernel2004")) { createChart( "#kernel2004", @@ -167,6 +177,10 @@ function clearCharts() { if (kernelEol) { kernelEol.innerHTML = ""; } + const kernel2204 = document.querySelector("#kernel2204"); + if (kernel2204) { + kernel2204.innerHTML = ""; + } const kernel2004 = document.querySelector("#kernel2004"); if (kernel2004) { kernel2004.innerHTML = ""; @@ -205,7 +219,7 @@ function clearCharts() { } } -var mediumBreakpoint = 875; +var mediumBreakpoint = 620; // A bit of a hack, but chart doesn't load with full year axis on first load, // It has to be loaded once, and then again diff --git a/static/js/src/tabbed-content.js b/static/js/src/tabbed-content.js index 15a843b4810..10e4206cb12 100644 --- a/static/js/src/tabbed-content.js +++ b/static/js/src/tabbed-content.js @@ -112,22 +112,25 @@ tabContent.forEach((content) => { if (tabElement === tab) { tabElement.setAttribute("aria-selected", true); - content.removeAttribute("hidden"); + content.classList.remove("u-hide"); + if (triggerReload) { + window.dispatchEvent(new Event("resize")); + } } else { tabElement.setAttribute("aria-selected", false); - content.setAttribute("hidden", true); + content.classList.add("u-hide"); } }); }); }; /** - Attaches events to tab links within a given parent element, - and sets the active tab if the current hash matches the id - of an element controlled by a tab link - @param {String} selector class name of the element - containing the tabs we want to attach events to - */ + Attaches events to tab links within a given parent element, + and sets the active tab if the current hash matches the id + of an element controlled by a tab link + @param {String} selector class name of the element + containing the tabs we want to attach events to + */ const initTabs = (selector) => { var tabContainers = [].slice.call(document.querySelectorAll(selector)); @@ -156,6 +159,34 @@ }); }; + /** + Check to see if a given script has been loaded on the page. + + If a chart is on a tab that isn't visible, it isn't built, + because its width depends on a parent element's width, which + is zero when the tab is not visible. + + Charts aren't responsive and need to be redrawn if the window + is resized, so they listen for that event and trigger the redraw + when it fires. + + @param {String} scriptName name of the script to check exists + on the page + */ + const isScriptIncluded = (scriptName) => { + var scripts = document.scripts; + + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].src.includes(scriptName)) { + return true; + } + } + + return false; + }; + const targetScript = "release-chart.js"; + const triggerReload = isScriptIncluded(targetScript); + document.addEventListener("DOMContentLoaded", () => { initTabs(".js-tabbed-content"); }); diff --git a/static/js/src/tabotronic.js b/static/js/src/tabotronic.js index d677a2c2d6f..64bde010a88 100644 --- a/static/js/src/tabotronic.js +++ b/static/js/src/tabotronic.js @@ -33,6 +33,10 @@ panel.classList.add("u-hide"); }); panelElement.classList.remove("u-hide"); + // Hack to fix a graph sizing bug: + // The size is defined by the container, but on hidden tabs it falls back + // on the next parent which is smaller than the container it is in when + // not hidden if (panelElement.querySelector("svg[class='chart']")) { window.dispatchEvent(new Event("resize")); } diff --git a/static/js/src/tests/intlTelInput.test.js b/static/js/src/tests/intlTelInput.test.js new file mode 100644 index 00000000000..aee956d2f9d --- /dev/null +++ b/static/js/src/tests/intlTelInput.test.js @@ -0,0 +1,77 @@ +import fetchMock from "jest-fetch-mock"; +import intlTelInput from "intl-tel-input"; +import setupIntlTelInput from "../intlTelInput"; +import { fireEvent } from "@testing-library/dom"; + +jest.mock("intl-tel-input"); +fetchMock.enableMocks(); + +describe("setupIntlTelInput", () => { + let phoneInput; + let mobileInput; + let errorElement; + + beforeEach(() => { + document.body.innerHTML = ` + +

    +
  • + +
    +
    +
    +
    + + +
    +
  • +
`; + + phoneInput = document.querySelector("input#phone"); + mobileInput = document.querySelector(".iti"); + setupIntlTelInput(phoneInput); + fetch.resetMocks(); + intlTelInput.mockClear(); + }); + + it("imports intlTelInput correctly", () => { + expect(intlTelInput).toBeDefined(); + }); + + it("calls intlTelInput with correct parameters", async () => { + const expectedOptions = { + utilsScript: expect.any(String), + separateDialCode: expect.any(Boolean), + hiddenInput: expect.any(String), + initialCountry: expect.any(String), + geoIpLookup: expect.any(Function), + }; + + fetch.mockResponseOnce(JSON.stringify({ country_code: "gb" })); + + setupIntlTelInput(phoneInput); + + expect(intlTelInput).toBeCalledWith(phoneInput, expectedOptions); + }); + + it("validates phone number correctly", async () => { + setupIntlTelInput(phoneInput); + + phoneInput.value = "1-2,3.4(5)6/"; + const blurEvent = new Event("blur"); + phoneInput.dispatchEvent(blurEvent); + expect(mobileInput.parentNode.classList.contains("is-error")).toBeFalsy(); + + phoneInput.value = "abcdef"; + phoneInput.dispatchEvent(blurEvent); + expect(mobileInput.parentNode.classList.contains("is-error")).toBeTruthy(); + }); + + it("displays error message for an invalid phone number", () => { + phoneInput.value = "abcdef"; + fireEvent.blur(phoneInput); + + const errorMsg = document.querySelector(".p-form-validation__message"); + expect(errorMsg).toBeTruthy(); + }); +}); diff --git a/static/sass/_pattern_tabs.scss b/static/sass/_pattern_tabs.scss index a416a37638d..466ea0cd49e 100644 --- a/static/sass/_pattern_tabs.scss +++ b/static/sass/_pattern_tabs.scss @@ -55,7 +55,7 @@ } } - &.is-risc-v { + &.is-black { margin-left: 0; padding-left: 0; @@ -65,6 +65,18 @@ } } } + + &.on-paper { + .p-tabs__item { + background-color: #f7f7f7; + + &[aria-selected="true"] { + @include vf-highlight-bar($color-brand, left); + + background-color: white; + } + } + } } // Override the highlighted tab colour for pages that need to use the brand diff --git a/static/sass/styles.scss b/static/sass/styles.scss index 3cdcf8018e5..b6d1b1db5d5 100644 --- a/static/sass/styles.scss +++ b/static/sass/styles.scss @@ -1330,9 +1330,14 @@ hr.p-separator.is-shallow { background: #f3f3f3; } -// XXX Pete -// Custom style for black, underlined, links -a.is-black { - color: #111; - text-decoration: underline; +.amd-xilinx-table { + th:nth-child(2) { + width: 65%; + } + + @media screen and (max-width: $breakpoint-small - 1) { + th:nth-child(2) { + width: 50%; + } + } } diff --git a/templates/about/release-cycle.html b/templates/about/release-cycle.html index e0515e4ddce..d4f8a1062f6 100755 --- a/templates/about/release-cycle.html +++ b/templates/about/release-cycle.html @@ -420,7 +420,6 @@

Canonical Kubernetes release cycle

The release cycles of Charmed Kubernetes and MicroK8s are tightly synchronised to the release of upstream Kubernetes®. The current release and two prior releases are supported (N-2), subject to changes in the upstream release cycle. Canonical also provides expanded security maintenance (ESM) for N-4 Kubernetes releases in the stable release channel.

For Ubuntu Advantage support, users must upgrade to remain in the N-2 support window.

-

ESM support includes fixes to high and critical CVEs related to Kubernetes charms, limited to the LTS Ubuntu releases in the current N-4 support window. Upstream updates and fixes to Kubernetes binaries or container images are absorbed by Charmed Kubernetes and Microk8s.

The Canonical Kubernetes support lifecycle can be represented this way:

@@ -538,28 +537,4 @@

Canonical Kubernetes release cycle

- - {% endblock content %} diff --git a/templates/ai/what-is-kubeflow.html b/templates/ai/what-is-kubeflow.html index f1339df009d..001a8bf02fe 100644 --- a/templates/ai/what-is-kubeflow.html +++ b/templates/ai/what-is-kubeflow.html @@ -613,12 +613,7 @@

Get started today

- {% with first_item="_cloud_bootstack", second_item="_cloud_ubuntu_advantage", third_item="_cloud_further_reading" %}{% include "shared/contextual_footers/_contextual_footer.html" %}{% endwith %} - -
-
- {% endblock content %} \ No newline at end of file diff --git a/templates/azure/index.html b/templates/azure/index.html index cf6f5263341..cbc31e6a3a2 100644 --- a/templates/azure/index.html +++ b/templates/azure/index.html @@ -45,18 +45,6 @@

Ubuntu on Azure powers cloud leaders like

-
- {{ image ( - url="https://assets.ubuntu.com/v1/92cf9e91-walmart-logo.png", - alt="Walmart", - width="288", - height="288", - hi_def=True, - loading="lazy", - attrs={"class": "p-logo-section__logo"} - ) | safe - }} -
{{ image ( url="https://assets.ubuntu.com/v1/6cd78b57-at%26t-logo.png", diff --git a/templates/core/features/index.html b/templates/core/features/index.html index 484d59ee918..fb8876310d7 100644 --- a/templates/core/features/index.html +++ b/templates/core/features/index.html @@ -280,7 +280,7 @@

Snaps

-