diff --git a/packages/react-native/Libraries/Blob/URL.js b/packages/react-native/Libraries/Blob/URL.js index 395e99d860a888..c9f6d933a556e9 100644 --- a/packages/react-native/Libraries/Blob/URL.js +++ b/packages/react-native/Libraries/Blob/URL.js @@ -107,15 +107,21 @@ export class URL { } get hash(): string { - throw new Error('URL.hash is not implemented'); + const hashMatch = this._url.match(/#([^/]*)/); + return hashMatch ? `#${hashMatch[1]}` : ''; } get host(): string { - throw new Error('URL.host is not implemented'); + const hostMatch = this._url.match(/^https?:\/\/(?:[^@]+@)?([^:/?#]+)/); + const portMatch = this._url.match(/:(\d+)(?=[/?#]|$)/); + return hostMatch + ? hostMatch[1] + (portMatch ? `:${portMatch[1]}` : '') + : ''; } get hostname(): string { - throw new Error('URL.hostname is not implemented'); + const hostnameMatch = this._url.match(/^https?:\/\/(?:[^@]+@)?([^:/?#]+)/); + return hostnameMatch ? hostnameMatch[1] : ''; } get href(): string { @@ -123,27 +129,33 @@ export class URL { } get origin(): string { - throw new Error('URL.origin is not implemented'); + const matches = this._url.match(/^(https?:\/\/[^/]+)/); + return matches ? matches[1] : ''; } get password(): string { - throw new Error('URL.password is not implemented'); + const passwordMatch = this._url.match(/https?:\/\/.*:(.*)@/); + return passwordMatch ? passwordMatch[1] : ''; } get pathname(): string { - throw new Error('URL.pathname not implemented'); + const pathMatch = this._url.match(/https?:\/\/[^/]+(\/[^?#]*)?/); + return pathMatch ? pathMatch[1] || '/' : '/'; } get port(): string { - throw new Error('URL.port is not implemented'); + const portMatch = this._url.match(/:(\d+)(?=[/?#]|$)/); + return portMatch ? portMatch[1] : ''; } get protocol(): string { - throw new Error('URL.protocol is not implemented'); + const protocolMatch = this._url.match(/^([a-zA-Z][a-zA-Z\d+\-.]*):/); + return protocolMatch ? protocolMatch[1] + ':' : ''; } get search(): string { - throw new Error('URL.search is not implemented'); + const searchMatch = this._url.match(/\?([^#]*)/); + return searchMatch ? `?${searchMatch[1]}` : ''; } get searchParams(): URLSearchParams { @@ -168,6 +180,7 @@ export class URL { } get username(): string { - throw new Error('URL.username is not implemented'); + const usernameMatch = this._url.match(/^https?:\/\/([^:@]+)(?::[^@]*)?@/); + return usernameMatch ? usernameMatch[1] : ''; } } diff --git a/packages/react-native/Libraries/Blob/__tests__/URL-test.js b/packages/react-native/Libraries/Blob/__tests__/URL-test.js index c317217c8fe724..a0d3be1b6ab1ab 100644 --- a/packages/react-native/Libraries/Blob/__tests__/URL-test.js +++ b/packages/react-native/Libraries/Blob/__tests__/URL-test.js @@ -40,5 +40,17 @@ describe('URL', function () { // Insert / between Base and Path if missing const k = new URL('en-US/docs', 'https://developer.mozilla.org'); expect(k.href).toBe('https://developer.mozilla.org/en-US/docs'); + //More cases + const url = new URL( + 'https://username:password@reactnative.dev:8080/docs/path?query=testQuery&key=value#fragment', + ); + expect(url.hash).toBe('#fragment'); + expect(url.host).toBe('reactnative.dev:8080'); + expect(url.hostname).toBe('reactnative.dev'); + expect(url.password).toBe('password'); + expect(url.username).toBe('username'); + expect(url.pathname).toBe('/docs/path'); + expect(url.port).toBe('8080'); + expect(url.search).toBe('?query=testQuery&key=value'); }); }); diff --git a/packages/rn-tester/js/examples/Urls/UrlExample.js b/packages/rn-tester/js/examples/Urls/UrlExample.js new file mode 100644 index 00000000000000..62ee124948ba8c --- /dev/null +++ b/packages/rn-tester/js/examples/Urls/UrlExample.js @@ -0,0 +1,92 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; + +import RNTesterText from '../../components/RNTesterText'; +import React from 'react'; +import {View, StyleSheet} from 'react-native'; + +type Props = { + url: string, +}; + +function URLComponent(props: Props) { + const parsedUrl = new URL(props.url); + return ( + + {`href: ${parsedUrl.href}`} + {`hash: ${parsedUrl.hash}`} + {`host: ${parsedUrl.host}`} + {`hostname: ${parsedUrl.hostname}`} + {`password: ${parsedUrl.password}`} + {`username: ${parsedUrl.username}`} + {`pathname: ${parsedUrl.pathname}`} + {`port: ${parsedUrl.port}`} + {`search: ${parsedUrl.search}`} + + ); +} + +const styles = StyleSheet.create({ + container: { + padding: 10, + }, +}); + +exports.title = 'URL'; +exports.category = 'Basic'; +exports.description = 'URL Parameters test'; +exports.examples = [ + { + title: 'completeURL', + description: 'URL with username,password,port,and queryparams', + render(): React.Node { + return ( + + ); + }, + }, + { + title: 'basicURL', + description: 'Basic URL without username, password, or port', + render(): React.Node { + return ; + }, + }, + { + title: 'queryParamsURL', + description: 'URL with query parameters', + render(): React.Node { + return ( + + ); + }, + }, + { + title: 'authAndPortURL', + description: 'URL with username, password, and port', + render(): React.Node { + return ( + + ); + }, + }, +] as Array; diff --git a/packages/rn-tester/js/utils/RNTesterList.android.js b/packages/rn-tester/js/utils/RNTesterList.android.js index cde36a290befc9..5dd22bef5ae3c1 100644 --- a/packages/rn-tester/js/utils/RNTesterList.android.js +++ b/packages/rn-tester/js/utils/RNTesterList.android.js @@ -187,6 +187,11 @@ const APIs: Array = ([ category: 'Basic', module: require('../examples/AppState/AppStateExample'), }, + { + key: 'URLExample', + category: 'Basic', + module: require('../examples/Urls/UrlExample'), + }, { key: 'BorderExample', category: 'UI', diff --git a/packages/rn-tester/js/utils/RNTesterList.ios.js b/packages/rn-tester/js/utils/RNTesterList.ios.js index 6f9f1456cb6e9f..00514ce0201139 100644 --- a/packages/rn-tester/js/utils/RNTesterList.ios.js +++ b/packages/rn-tester/js/utils/RNTesterList.ios.js @@ -192,6 +192,11 @@ const APIs: Array = ([ key: 'AppStateExample', module: require('../examples/AppState/AppStateExample'), }, + { + key: 'URLExample', + category: 'Basic', + module: require('../examples/Urls/UrlExample'), + }, { key: 'BorderExample', module: require('../examples/Border/BorderExample').default,