From dc8c58a1fc19eec8e98dc15be87c435577a34578 Mon Sep 17 00:00:00 2001 From: Awais Ansari Date: Fri, 6 Mar 2026 16:15:34 +0500 Subject: [PATCH 1/2] feat: Add HeaderNotificationsSlot to enable notifications tray default ON --- package-lock.json | 444 +++++++++++++++--- package.json | 1 + src/Header.test.jsx | 5 +- src/desktop-header/DesktopHeader.jsx | 2 + src/learning-header/LearningHeader.jsx | 2 + src/mobile-header/MobileHeader.jsx | 2 + .../HeaderNotificationsSlot/README.md | 114 +++++ .../HeaderNotificationsSlot/index.jsx | 14 + src/plugin-slots/README.md | 1 + src/studio-header/HeaderBody.tsx | 2 + 10 files changed, 533 insertions(+), 54 deletions(-) create mode 100644 src/plugin-slots/HeaderNotificationsSlot/README.md create mode 100644 src/plugin-slots/HeaderNotificationsSlot/index.jsx diff --git a/package-lock.json b/package-lock.json index 1237fe4a3d..7556530880 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0-semantically-released", "license": "AGPL-3.0", "dependencies": { + "@edx/frontend-plugin-notifications": "^2.0.5", "@fortawesome/fontawesome-svg-core": "6.7.2", "@fortawesome/free-brands-svg-icons": "6.7.2", "@fortawesome/free-regular-svg-icons": "6.7.2", @@ -125,7 +126,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", @@ -444,7 +444,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2479,6 +2478,371 @@ } } }, + "node_modules/@edx/frontend-plugin-notifications": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@edx/frontend-plugin-notifications/-/frontend-plugin-notifications-2.0.5.tgz", + "integrity": "sha512-S9GpOHXumF+CsRDs2VQYpkacHAAXVZ9GI7WYSZEHYjKqEsRifc5ojre6GJXx20hwnMYSfasBTaDU8InAlpwwLQ==", + "license": "AGPL-3.0", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-brands-svg-icons": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", + "classnames": "2.5.1", + "dompurify": "^3.1.7", + "jest-environment-jsdom": "^29.7.0", + "lodash": "4.17.21", + "timeago.js": "4.0.2", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "@edx/frontend-platform": "^8.0.0", + "@openedx/paragon": "^22.0.0 || ^23.0.0", + "prop-types": "^15.5.10", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-router-dom": "^6.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "license": "MIT" + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@edx/frontend-plugin-notifications/node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, "node_modules/@edx/new-relic-source-map-webpack-plugin": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-2.1.0.tgz", @@ -5267,7 +5631,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "devOptional": true, "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -5573,7 +5936,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "devOptional": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -8044,14 +8406,12 @@ "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "devOptional": true, "license": "MIT" }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "devOptional": true, "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" @@ -8455,7 +8815,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "devOptional": true, "license": "MIT", "engines": { "node": ">= 10" @@ -8727,14 +9086,12 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" @@ -8744,7 +9101,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" @@ -8847,7 +9203,6 @@ "version": "24.5.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~7.12.0" @@ -8978,7 +9333,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/testing-library__jest-dom": { @@ -8995,9 +9349,15 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "devOptional": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -9024,7 +9384,6 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -9034,7 +9393,6 @@ "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "devOptional": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -9807,7 +10165,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "deprecated": "Use your platform's native atob() and btoa() methods instead", - "devOptional": true, "license": "BSD-3-Clause" }, "node_modules/accepts": { @@ -9828,7 +10185,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "devOptional": true, "license": "MIT", "peer": true, "bin": { @@ -9842,7 +10198,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "devOptional": true, "license": "MIT", "dependencies": { "acorn": "^8.1.0", @@ -9876,7 +10231,6 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "devOptional": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -12662,7 +13016,6 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "devOptional": true, "license": "MIT" }, "node_modules/cssstyle": { @@ -12771,7 +13124,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "devOptional": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -12789,7 +13141,6 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "devOptional": true, "license": "MIT" }, "node_modules/decode-uri-component": { @@ -13206,7 +13557,6 @@ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", "deprecated": "Use your platform's native DOMException instead", - "devOptional": true, "license": "MIT", "dependencies": { "webidl-conversions": "^7.0.0" @@ -13231,6 +13581,18 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz", + "integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -13419,7 +13781,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -13675,7 +14036,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "devOptional": true, "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", @@ -13697,7 +14057,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "optional": true, "engines": { @@ -14603,7 +14962,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -14613,7 +14971,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -17163,7 +17520,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "devOptional": true, "license": "MIT" }, "node_modules/is-regex": { @@ -18868,7 +19224,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", @@ -18889,7 +19244,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -18902,7 +19256,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "devOptional": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -18917,14 +19270,12 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "devOptional": true, "license": "MIT" }, "node_modules/jest-message-util/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -20345,7 +20696,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "devOptional": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -20363,7 +20713,6 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "devOptional": true, "funding": [ { "type": "github", @@ -21542,7 +21891,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "devOptional": true, "license": "MIT" }, "node_modules/multicast-dns": { @@ -21722,7 +22070,6 @@ "version": "2.2.22", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==", - "devOptional": true, "license": "MIT" }, "node_modules/object-assign": { @@ -22172,7 +22519,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "devOptional": true, "license": "MIT", "dependencies": { "entities": "^4.4.0" @@ -23662,7 +24008,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "devOptional": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -23681,7 +24026,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -23811,7 +24155,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "devOptional": true, "license": "MIT" }, "node_modules/queue-microtask": { @@ -24815,7 +25158,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "devOptional": true, "license": "MIT" }, "node_modules/resolve": { @@ -25207,7 +25549,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "devOptional": true, "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -25970,7 +26311,6 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "devOptional": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" @@ -25983,7 +26323,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -26522,7 +26861,6 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "devOptional": true, "license": "MIT" }, "node_modules/synckit": { @@ -26725,6 +27063,12 @@ "devOptional": true, "license": "MIT" }, + "node_modules/timeago.js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz", + "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==", + "license": "MIT" + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -27122,7 +27466,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=4" @@ -27328,7 +27671,6 @@ "version": "7.12.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", - "devOptional": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -27564,7 +27906,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "devOptional": true, "license": "MIT", "dependencies": { "querystringify": "^2.1.1", @@ -27778,7 +28119,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -28386,7 +28726,6 @@ "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -28418,7 +28757,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "devOptional": true, "license": "MIT" }, "node_modules/xregexp": { diff --git a/package.json b/package.json index bace217d75..a0cd220d0a 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "ts-jest": "^29.4.4" }, "dependencies": { + "@edx/frontend-plugin-notifications": "^2.0.5", "@fortawesome/fontawesome-svg-core": "6.7.2", "@fortawesome/free-brands-svg-icons": "6.7.2", "@fortawesome/free-regular-svg-icons": "6.7.2", diff --git a/src/Header.test.jsx b/src/Header.test.jsx index 3b4cc8a4c2..6efeb33444 100644 --- a/src/Header.test.jsx +++ b/src/Header.test.jsx @@ -3,6 +3,7 @@ import React from 'react'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import TestRenderer from 'react-test-renderer'; import { AppContext } from '@edx/frontend-platform/react'; +import { MemoryRouter } from 'react-router-dom'; import { Context as ResponsiveContext } from 'react-responsive'; import Header from './index'; @@ -13,7 +14,9 @@ const HeaderComponent = ({ width, contextValue }) => ( -
+ +
+ diff --git a/src/desktop-header/DesktopHeader.jsx b/src/desktop-header/DesktopHeader.jsx index 5828ae6da3..e8f32b7b79 100644 --- a/src/desktop-header/DesktopHeader.jsx +++ b/src/desktop-header/DesktopHeader.jsx @@ -14,6 +14,7 @@ import DesktopMainMenuSlot from '../plugin-slots/DesktopMainMenuSlot'; import { desktopHeaderMainOrSecondaryMenuDataShape } from './DesktopHeaderMainOrSecondaryMenu'; import DesktopSecondaryMenuSlot from '../plugin-slots/DesktopSecondaryMenuSlot'; import DesktopUserMenuSlot from '../plugin-slots/DesktopUserMenuSlot'; +import HeaderNotificationsSlot from '../plugin-slots/HeaderNotificationsSlot'; import { desktopUserMenuDataShape } from './DesktopHeaderUserMenu'; // i18n @@ -78,6 +79,7 @@ const DesktopHeader = ({ {loggedIn ? ( <> + {renderSecondaryMenu()} {renderUserMenu()} diff --git a/src/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index 349690bb2e..6d3411b30a 100644 --- a/src/learning-header/LearningHeader.jsx +++ b/src/learning-header/LearningHeader.jsx @@ -11,6 +11,7 @@ import CourseInfoSlot from '../plugin-slots/CourseInfoSlot'; import { courseInfoDataShape } from './LearningHeaderCourseInfo'; import messages from './messages'; import LearningHelpSlot from '../plugin-slots/LearningHelpSlot'; +import HeaderNotificationsSlot from '../plugin-slots/HeaderNotificationsSlot'; const LearningHeader = ({ courseOrg, @@ -39,6 +40,7 @@ const LearningHeader = ({ {showUserDropdown && authenticatedUser && ( <> + {userMenu.length > 0 || loggedOutItems.length > 0 ? (
+ {loggedIn && } ( + 📢 + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_notifications_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( + 💬 + ), + }, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/HeaderNotificationsSlot/index.jsx b/src/plugin-slots/HeaderNotificationsSlot/index.jsx new file mode 100644 index 0000000000..af5c242482 --- /dev/null +++ b/src/plugin-slots/HeaderNotificationsSlot/index.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import NotificationsTray from '@edx/frontend-plugin-notifications'; + +const HeaderNotificationsSlot = () => ( + + + +); + +export default HeaderNotificationsSlot; diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index c693e36843..9c90823d09 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -2,6 +2,7 @@ ### Shared * [`org.openedx.frontend.layout.header_logo.v1`](./LogoSlot/) +* [`org.openedx.frontend.layout.header_notifications_tray.v1`](./HeaderNotificationsSlot/) ### Desktop Header * [`org.openedx.frontend.layout.header_desktop.v1`](./DesktopHeaderSlot/) diff --git a/src/studio-header/HeaderBody.tsx b/src/studio-header/HeaderBody.tsx index 4037240ac5..eccdf90917 100644 --- a/src/studio-header/HeaderBody.tsx +++ b/src/studio-header/HeaderBody.tsx @@ -14,6 +14,7 @@ import UserMenu from './UserMenu'; import BrandNav from './BrandNav'; import NavDropdownMenu from './NavDropdownMenu'; import StudioHeaderSearchButtonSlot from '../plugin-slots/StudioHeaderSearchButtonSlot'; +import HeaderNotificationsSlot from '../plugin-slots/HeaderNotificationsSlot'; export interface HeaderBodyProps { studioBaseUrl: string; @@ -137,6 +138,7 @@ const HeaderBody = ({ )} + From 3c5108253f602f35bd9edd12b1f6e997ce9eabe1 Mon Sep 17 00:00:00 2001 From: Awais Ansari Date: Thu, 12 Mar 2026 03:21:37 +0500 Subject: [PATCH 2/2] refactor: Embed notifications in wrapper slots for better customization --- src/desktop-header/DesktopHeader.jsx | 2 - src/learning-header/LearningHeader.jsx | 6 +- src/mobile-header/MobileHeader.jsx | 2 - .../DesktopSecondaryMenuSlot/README.md | 30 ++++ .../DesktopSecondaryMenuSlot/index.jsx | 2 + .../HeaderNotificationsSlot/README.md | 147 +++++++++++++++--- .../LearningHeaderActionsSlot/README.md | 102 ++++++++++++ .../LearningHeaderActionsSlot/index.jsx | 16 ++ src/plugin-slots/README.md | 6 +- .../StudioHeaderActionsSlot/README.md | 102 ++++++++++++ .../StudioHeaderActionsSlot/index.tsx | 22 +++ src/studio-header/HeaderBody.tsx | 7 +- 12 files changed, 404 insertions(+), 40 deletions(-) create mode 100644 src/plugin-slots/LearningHeaderActionsSlot/README.md create mode 100644 src/plugin-slots/LearningHeaderActionsSlot/index.jsx create mode 100644 src/plugin-slots/StudioHeaderActionsSlot/README.md create mode 100644 src/plugin-slots/StudioHeaderActionsSlot/index.tsx diff --git a/src/desktop-header/DesktopHeader.jsx b/src/desktop-header/DesktopHeader.jsx index e8f32b7b79..5828ae6da3 100644 --- a/src/desktop-header/DesktopHeader.jsx +++ b/src/desktop-header/DesktopHeader.jsx @@ -14,7 +14,6 @@ import DesktopMainMenuSlot from '../plugin-slots/DesktopMainMenuSlot'; import { desktopHeaderMainOrSecondaryMenuDataShape } from './DesktopHeaderMainOrSecondaryMenu'; import DesktopSecondaryMenuSlot from '../plugin-slots/DesktopSecondaryMenuSlot'; import DesktopUserMenuSlot from '../plugin-slots/DesktopUserMenuSlot'; -import HeaderNotificationsSlot from '../plugin-slots/HeaderNotificationsSlot'; import { desktopUserMenuDataShape } from './DesktopHeaderUserMenu'; // i18n @@ -79,7 +78,6 @@ const DesktopHeader = ({ {loggedIn ? ( <> - {renderSecondaryMenu()} {renderUserMenu()} diff --git a/src/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index 6d3411b30a..9d70ee28f6 100644 --- a/src/learning-header/LearningHeader.jsx +++ b/src/learning-header/LearningHeader.jsx @@ -10,8 +10,7 @@ import LogoSlot from '../plugin-slots/LogoSlot'; import CourseInfoSlot from '../plugin-slots/CourseInfoSlot'; import { courseInfoDataShape } from './LearningHeaderCourseInfo'; import messages from './messages'; -import LearningHelpSlot from '../plugin-slots/LearningHelpSlot'; -import HeaderNotificationsSlot from '../plugin-slots/HeaderNotificationsSlot'; +import LearningHeaderActionsSlot from '../plugin-slots/LearningHeaderActionsSlot'; const LearningHeader = ({ courseOrg, @@ -40,8 +39,7 @@ const LearningHeader = ({
{showUserDropdown && authenticatedUser && ( <> - - + diff --git a/src/mobile-header/MobileHeader.jsx b/src/mobile-header/MobileHeader.jsx index a3fcb9bf61..6d48efd073 100644 --- a/src/mobile-header/MobileHeader.jsx +++ b/src/mobile-header/MobileHeader.jsx @@ -13,7 +13,6 @@ import MobileMainMenuSlot from '../plugin-slots/MobileMainMenuSlot'; import { mobileHeaderMainMenuDataShape } from './MobileHeaderMainMenu'; import MobileUserMenuSlot from '../plugin-slots/MobileUserMenuSlot'; import { mobileHeaderUserMenuDataShape } from './MobileHeaderUserMenu'; -import HeaderNotificationsSlot from '../plugin-slots/HeaderNotificationsSlot'; // i18n import messages from '../Header.messages'; @@ -81,7 +80,6 @@ const MobileHeader = ({ {userMenu.length > 0 || loggedOutItems.length > 0 ? (
- {loggedIn && } + ); diff --git a/src/plugin-slots/HeaderNotificationsSlot/README.md b/src/plugin-slots/HeaderNotificationsSlot/README.md index 16a0c87d1e..69793e0fd0 100644 --- a/src/plugin-slots/HeaderNotificationsSlot/README.md +++ b/src/plugin-slots/HeaderNotificationsSlot/README.md @@ -7,20 +7,44 @@ ## Description -This slot renders the notifications tray (bell icon + notification popover) from `@edx/frontend-plugin-notifications` by default. It is present in **all header types** for authenticated users: +This slot renders the notifications tray (bell icon + notification popover) from `@edx/frontend-plugin-notifications` by default. -- **Desktop Header** — before the secondary menu, to the left of menu items and user dropdown -- **Mobile Header** — before the user menu trigger (avatar) -- **Learning Header** — before the help link, to the left of help and user dropdown -- **Studio Header** — before the search button, to the left of search and user menu (both desktop and mobile) +**Important:** This slot is **not used standalone** in headers. Instead, it is embedded within the default content of three parent slots: -Notifications are **enabled by default** for all community instances. The `NotificationsTray` component is self-gating: it renders nothing when the backend waffle flag is disabled. +1. **Desktop Header** — via `org.openedx.frontend.layout.header_desktop_secondary_menu.v1` + Notifications appear before secondary menu items (e.g., "New", "Help") + +2. **Learning Header** — via `org.openedx.frontend.layout.learning_header_actions.v1` +Notifications are **enabled by default** for authenticated users in all three headers. The `NotificationsTray` component is self-gating: it renders nothing when the backend waffle flag `DISABLE_NOTIFICATIONS` is enabled. + +### Mobile Headers + +Mobile header support for LMS (non-Learning) is **not included** in this implementation. Adding notifications to the mobile header requires design/product review due to limited viewport space. This will be addressed in a future PR. + +## Slot Hierarchy + +``` +Desktop Header +└── org.openedx.frontend.layout.header_desktop_secondary_menu.v1 + ├── org.openedx.frontend.layout.header_notifications_tray.v1 ← This slot + └── (secondary menu items) + +Learning Header +└── org.openedx.frontend.layout.learning_header_actions.v1 + ├── org.openedx.frontend.layout.header_notifications_tray.v1 ← This slot + └── org.openedx.frontend.layout.header_learning_help.v1 + +Studio Header +└── org.openedx.frontend.layout.studio_header_actions.v1 + ├── org.openedx.frontend.layout.header_notifications_tray.v1 ← This slot + └── org.openedx.frontend.layout.studio_header_search_button_slot.v1 +``` ## Examples -### Disable Notifications +### Disable Notifications Globally (All Headers) -The following `env.config.jsx` will hide the notifications tray entirely. +The following `env.config.jsx` will hide the notifications tray across all headers: ```jsx import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; @@ -42,9 +66,77 @@ const config = { export default config; ``` -### Replace Notifications with Custom Component +### Disable Notifications in Desktop Header Only + +The following `env.config.jsx` will hide notifications only in the Desktop header: + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_desktop_secondary_menu.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'header_notifications_tray', + }, + ] + }, + }, +} + +export default config; +``` + +### Disable Notifications in Learning Header Only + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'header_notifications_tray', + }, + ] + }, + }, +} + +export default config; +``` + +### Disable Notifications in Studio Header Only + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.studio_header_actions.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'header_notifications_tray', + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Notifications with Custom Component (All Headers) -The following `env.config.jsx` will replace the default notifications tray with a custom component. +The following `env.config.jsx` will replace the default notifications tray with a custom component across all headers: ```jsx import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; @@ -71,9 +163,9 @@ const config = { export default config; ``` -### Add Custom Components before and after Notifications +### Add Custom Alert Icon Before Notifications (All Headers) -The following `env.config.jsx` will place custom components before and after the notifications tray. +The following `env.config.jsx` will insert a custom alert icon before the notifications bell: ```jsx import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; @@ -86,22 +178,11 @@ const config = { { op: PLUGIN_OPERATIONS.Insert, widget: { - id: 'custom_before_notifications_component', + id: 'custom_alert_icon', type: DIRECT_PLUGIN, priority: 10, RenderWidget: () => ( - 📢 - ), - }, - }, - { - op: PLUGIN_OPERATIONS.Insert, - widget: { - id: 'custom_after_notifications_component', - type: DIRECT_PLUGIN, - priority: 90, - RenderWidget: () => ( - 💬 + ), }, }, @@ -112,3 +193,19 @@ const config = { export default config; ``` + +## Backend Waffle Flag + +The `NotificationsTray` component respects the backend waffle flag `DISABLE_NOTIFICATIONS`. When this flag is enabled, the notification bell will not render regardless of frontend configuration. + +**Backend PR:** [openedx/openedx-platform#38073](https://github.com/openedx/openedx-platform/pull/38073) + +## Migration from tutor-contrib-platform-notifications + +Sites currently using `tutor-contrib-platform-notifications` can remove these plugin configurations after upgrading to this version: + +- `org.openedx.frontend.layout.header_desktop_secondary_menu.v1` (notifications insertion) +- `org.openedx.frontend.layout.studio_header_search_button_slot.v1` (notifications insertion) +- `org.openedx.frontend.layout.header_learning_help.v1` (notifications insertion) + +Notifications will work out-of-the-box without any plugin configuration. If you want to disable them, follow the examples above. diff --git a/src/plugin-slots/LearningHeaderActionsSlot/README.md b/src/plugin-slots/LearningHeaderActionsSlot/README.md new file mode 100644 index 0000000000..c2871b6e7d --- /dev/null +++ b/src/plugin-slots/LearningHeaderActionsSlot/README.md @@ -0,0 +1,102 @@ +# Learning Header Actions Slot + +### Slot ID: `org.openedx.frontend.layout.learning_header_actions.v1` + +### Slot ID Aliases +* `learning_header_actions_slot` + +## Description + +This slot wraps the notification tray and help link in the Learning header. It provides a single location for operators to customize the action area before the user menu in the Learning (course) header. + +By default, this slot renders: +1. **HeaderNotificationsSlot** — The notification bell from `@edx/frontend-plugin-notifications` +2. **LearningHelpSlot** — The help link (question mark icon) + +This wrapper slot ensures that: +- Notifications are enabled by default in the Learning header +- Operators can customize the entire action area using one slot ID +- The existing `org.openedx.frontend.layout.header_learning_help.v1` remains functional for backward compatibility + +## Examples + +### Disable Notifications in Learning Header Only + +The following `env.config.jsx` will hide the notification bell in the Learning header while keeping it in Desktop and Studio headers: + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'header_notifications_tray', + }, + ] + }, + }, +} + +export default config; +``` + +### Replace the Entire Actions Area + +The following `env.config.jsx` will replace both notifications and help link with a custom component: + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v1': { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_learning_actions', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +
+ + +
+ ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Modify Help Link Only + +To customize just the help link while keeping notifications, use the nested slot: + +```jsx +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_learning_help.v1': { + // Your help link customization + }, + }, +} + +export default config; +``` + +## Slot Hierarchy + +This slot contains two child slots: +- `org.openedx.frontend.layout.header_notifications_tray.v1` (HeaderNotificationsSlot) +- `org.openedx.frontend.layout.header_learning_help.v1` (LearningHelpSlot) + +Operators can customize at either the parent level (this slot) or the child level for more granular control. diff --git a/src/plugin-slots/LearningHeaderActionsSlot/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/index.jsx new file mode 100644 index 0000000000..7d833630ab --- /dev/null +++ b/src/plugin-slots/LearningHeaderActionsSlot/index.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import HeaderNotificationsSlot from '../HeaderNotificationsSlot'; +import LearningHelpSlot from '../LearningHelpSlot'; + +const LearningHeaderActionsSlot = () => ( + + + + +); + +export default LearningHeaderActionsSlot; diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index 9c90823d09..c8582e4145 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -2,18 +2,19 @@ ### Shared * [`org.openedx.frontend.layout.header_logo.v1`](./LogoSlot/) -* [`org.openedx.frontend.layout.header_notifications_tray.v1`](./HeaderNotificationsSlot/) +* [`org.openedx.frontend.layout.header_notifications_tray.v1`](./HeaderNotificationsSlot/) — Note: Embedded in slot defaults, not used standalone ### Desktop Header * [`org.openedx.frontend.layout.header_desktop.v1`](./DesktopHeaderSlot/) * [`org.openedx.frontend.layout.header_desktop_logged_out_items.v1`](./DesktopLoggedOutItemsSlot/) * [`org.openedx.frontend.layout.header_desktop_main_menu.v1`](./DesktopMainMenuSlot/) -* [`org.openedx.frontend.layout.header_desktop_secondary_menu.v1`](./DesktopSecondaryMenuSlot/) +* [`org.openedx.frontend.layout.header_desktop_secondary_menu.v1`](./DesktopSecondaryMenuSlot/) — Includes notifications by default * [`org.openedx.frontend.layout.header_desktop_user_menu.v1`](./DesktopUserMenuSlot/) * [`org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1`](./DesktopUserMenuToggleSlot/) ### Learning Header * [`org.openedx.frontend.layout.header_learning_course_info.v1`](./CourseInfoSlot/) +* [`org.openedx.frontend.layout.learning_header_actions.v1`](./LearningHeaderActionsSlot/) — Includes notifications and help by default * [`org.openedx.frontend.layout.header_learning_help.v1`](./LearningHelpSlot/) * [`org.openedx.frontend.layout.header_learning_logged_out_items.v1`](./LearningLoggedOutItemsSlot/) * [`org.openedx.frontend.layout.header_learning_user_menu.v1`](./LearningUserMenuSlot/) @@ -27,4 +28,5 @@ * [`org.openedx.frontend.layout.header_mobile_user_menu_trigger.v1`](./MobileUserMenuToggleSlot/) ### Studio Header +* [`org.openedx.frontend.layout.studio_header_actions.v1`](./StudioHeaderActionsSlot/) — Includes notifications and search by default * [`org.openedx.frontend.layout.studio_header_search_button_slot.v1`](./StudioHeaderSearchButtonSlot/) diff --git a/src/plugin-slots/StudioHeaderActionsSlot/README.md b/src/plugin-slots/StudioHeaderActionsSlot/README.md new file mode 100644 index 0000000000..4f472d2a10 --- /dev/null +++ b/src/plugin-slots/StudioHeaderActionsSlot/README.md @@ -0,0 +1,102 @@ +# Studio Header Actions Slot + +### Slot ID: `org.openedx.frontend.layout.studio_header_actions.v1` + +### Slot ID Aliases +* `studio_header_actions_slot` + +## Description + +This slot wraps the notification tray and search button in the Studio header (both desktop and mobile). It provides a single location for operators to customize the action area before the user menu in Studio. + +By default, this slot renders: +1. **HeaderNotificationsSlot** — The notification bell from `@edx/frontend-plugin-notifications` +2. **StudioHeaderSearchButtonSlot** — The search button (only visible when `searchButtonAction` is provided) + +This wrapper slot ensures that: +- Notifications are enabled by default in Studio +- Operators can customize the entire action area using one slot ID +- The existing `org.openedx.frontend.layout.studio_header_search_button_slot.v1` remains functional for backward compatibility + +## Examples + +### Disable Notifications in Studio Only + +The following `env.config.jsx` will hide the notification bell in Studio while keeping it in other headers: + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.studio_header_actions.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'header_notifications_tray', + }, + ] + }, + }, +} + +export default config; +``` + +### Replace the Entire Actions Area + +The following `env.config.jsx` will replace both notifications and search with a custom component: + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.studio_header_actions.v1': { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_studio_actions', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +
+ + +
+ ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Modify Search Button Only + +To customize just the search button while keeping notifications, use the nested slot: + +```jsx +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.studio_header_search_button_slot.v1': { + // Your search button customization + }, + }, +} + +export default config; +``` + +## Slot Hierarchy + +This slot contains two child slots: +- `org.openedx.frontend.layout.header_notifications_tray.v1` (HeaderNotificationsSlot) +- `org.openedx.frontend.layout.studio_header_search_button_slot.v1` (StudioHeaderSearchButtonSlot) + +Operators can customize at either the parent level (this slot) or the child level for more granular control. diff --git a/src/plugin-slots/StudioHeaderActionsSlot/index.tsx b/src/plugin-slots/StudioHeaderActionsSlot/index.tsx new file mode 100644 index 0000000000..37038aff2c --- /dev/null +++ b/src/plugin-slots/StudioHeaderActionsSlot/index.tsx @@ -0,0 +1,22 @@ +import React, { type FunctionComponent } from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import HeaderNotificationsSlot from '../HeaderNotificationsSlot'; +import StudioHeaderSearchButtonSlot from '../StudioHeaderSearchButtonSlot'; + +interface StudioHeaderActionsSlotProps { + searchButtonAction?: React.MouseEventHandler; +} + +const StudioHeaderActionsSlot: FunctionComponent = ({ + searchButtonAction, +}) => ( + + + + +); + +export default StudioHeaderActionsSlot; diff --git a/src/studio-header/HeaderBody.tsx b/src/studio-header/HeaderBody.tsx index eccdf90917..07e0b2d616 100644 --- a/src/studio-header/HeaderBody.tsx +++ b/src/studio-header/HeaderBody.tsx @@ -13,8 +13,7 @@ import CourseLockUp from './CourseLockUp'; import UserMenu from './UserMenu'; import BrandNav from './BrandNav'; import NavDropdownMenu from './NavDropdownMenu'; -import StudioHeaderSearchButtonSlot from '../plugin-slots/StudioHeaderSearchButtonSlot'; -import HeaderNotificationsSlot from '../plugin-slots/HeaderNotificationsSlot'; +import StudioHeaderActionsSlot from '../plugin-slots/StudioHeaderActionsSlot'; export interface HeaderBodyProps { studioBaseUrl: string; @@ -63,7 +62,6 @@ const HeaderBody = ({ searchButtonAction, containerProps = {}, }: HeaderBodyProps) => { - const renderBrandNav = ( )} - -